pacemaker  2.1.7-0f7f88312f
Scalable High-Availability cluster resource manager
rules.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2023 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 #include <crm/crm.h>
12 #include <crm/msg_xml.h>
13 #include <crm/common/xml.h>
15 
16 #include <glib.h>
17 
18 #include <crm/pengine/rules.h>
20 #include <crm/pengine/internal.h>
21 
22 #include <sys/types.h>
23 #include <regex.h>
24 #include <ctype.h>
25 
26 CRM_TRACE_INIT_DATA(pe_rules);
27 
38 gboolean
39 pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now,
40  crm_time_t *next_change)
41 {
42  pe_rule_eval_data_t rule_data = {
43  .node_hash = node_hash,
44  .role = pcmk_role_unknown,
45  .now = now,
46  .match_data = NULL,
47  .rsc_data = NULL,
48  .op_data = NULL
49  };
50 
51  return pe_eval_rules(ruleset, &rule_data, next_change);
52 }
53 
54 gboolean
55 pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
56  crm_time_t *now, crm_time_t *next_change,
57  pe_match_data_t *match_data)
58 {
59  pe_rule_eval_data_t rule_data = {
60  .node_hash = node_hash,
61  .role = role,
62  .now = now,
63  .match_data = match_data,
64  .rsc_data = NULL,
65  .op_data = NULL
66  };
67 
68  return pe_eval_expr(rule, &rule_data, next_change);
69 }
70 
87 gboolean
88 pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role,
89  crm_time_t *now, crm_time_t *next_change,
90  pe_match_data_t *match_data)
91 {
92  pe_rule_eval_data_t rule_data = {
93  .node_hash = node_hash,
94  .role = role,
95  .now = now,
96  .match_data = match_data,
97  .rsc_data = NULL,
98  .op_data = NULL
99  };
100 
101  return pe_eval_subexpr(expr, &rule_data, next_change);
102 }
103 
104 enum expression_type
105 find_expression_type(xmlNode * expr)
106 {
107  const char *attr = NULL;
108 
110 
111  if (pcmk__xe_is(expr, PCMK_XE_DATE_EXPRESSION)) {
112  return time_expr;
113 
114  } else if (pcmk__xe_is(expr, PCMK_XE_RSC_EXPRESSION)) {
115  return rsc_expr;
116 
117  } else if (pcmk__xe_is(expr, PCMK_XE_OP_EXPRESSION)) {
118  return op_expr;
119 
120  } else if (pcmk__xe_is(expr, XML_TAG_RULE)) {
121  return nested_rule;
122 
123  } else if (!pcmk__xe_is(expr, XML_TAG_EXPRESSION)) {
124  return not_expr;
125 
126  } else if (pcmk__str_any_of(attr, CRM_ATTR_UNAME, CRM_ATTR_KIND, CRM_ATTR_ID, NULL)) {
127  return loc_expr;
128 
129  } else if (pcmk__str_eq(attr, CRM_ATTR_ROLE, pcmk__str_none)) {
130  return role_expr;
131  }
132 
133  return attr_expr;
134 }
135 
136 /* As per the nethack rules:
137  *
138  * moon period = 29.53058 days ~= 30, year = 365.2422 days
139  * days moon phase advances on first day of year compared to preceding year
140  * = 365.2422 - 12*29.53058 ~= 11
141  * years in Metonic cycle (time until same phases fall on the same days of
142  * the month) = 18.6 ~= 19
143  * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
144  * (29 as initial condition)
145  * current phase in days = first day phase + days elapsed in year
146  * 6 moons ~= 177 days
147  * 177 ~= 8 reported phases * 22
148  * + 11/22 for rounding
149  *
150  * 0-7, with 0: new, 4: full
151  */
152 
153 static int
154 phase_of_the_moon(const crm_time_t *now)
155 {
156  uint32_t epact, diy, goldn;
157  uint32_t y;
158 
159  crm_time_get_ordinal(now, &y, &diy);
160 
161  goldn = (y % 19) + 1;
162  epact = (11 * goldn + 18) % 30;
163  if ((epact == 25 && goldn > 11) || epact == 24)
164  epact++;
165 
166  return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
167 }
168 
169 static int
170 check_one(const xmlNode *cron_spec, const char *xml_field, uint32_t time_field)
171 {
172  int rc = pcmk_rc_undetermined;
173  const char *value = crm_element_value(cron_spec, xml_field);
174  long long low, high;
175 
176  if (value == NULL) {
177  /* Return pe_date_result_undetermined if the field is missing. */
178  goto bail;
179  }
180 
181  if (pcmk__parse_ll_range(value, &low, &high) != pcmk_rc_ok) {
182  goto bail;
183  } else if (low == high) {
184  /* A single number was given, not a range. */
185  if (time_field < low) {
187  } else if (time_field > high) {
188  rc = pcmk_rc_after_range;
189  } else {
191  }
192  } else if (low != -1 && high != -1) {
193  /* This is a range with both bounds. */
194  if (time_field < low) {
196  } else if (time_field > high) {
197  rc = pcmk_rc_after_range;
198  } else {
200  }
201  } else if (low == -1) {
202  /* This is a range with no starting value. */
203  rc = time_field <= high ? pcmk_rc_within_range : pcmk_rc_after_range;
204  } else if (high == -1) {
205  /* This is a range with no ending value. */
206  rc = time_field >= low ? pcmk_rc_within_range : pcmk_rc_before_range;
207  }
208 
209 bail:
210  if (rc == pcmk_rc_within_range) {
211  crm_debug("Condition '%s' in %s: passed", value, xml_field);
212  } else {
213  crm_debug("Condition '%s' in %s: failed", value, xml_field);
214  }
215 
216  return rc;
217 }
218 
219 static gboolean
220 check_passes(int rc) {
221  /* _within_range is obvious. _undetermined is a pass because
222  * this is the return value if a field is not given. In this
223  * case, we just want to ignore it and check other fields to
224  * see if they place some restriction on what can pass.
225  */
226  return rc == pcmk_rc_within_range || rc == pcmk_rc_undetermined;
227 }
228 
229 #define CHECK_ONE(spec, name, var) do { \
230  int subpart_rc = check_one(spec, name, var); \
231  if (check_passes(subpart_rc) == FALSE) { \
232  return subpart_rc; \
233  } \
234 } while (0)
235 
236 int
237 pe_cron_range_satisfied(const crm_time_t *now, const xmlNode *cron_spec)
238 {
239  uint32_t h, m, s, y, d, w;
240 
241  CRM_CHECK(now != NULL, return pcmk_rc_op_unsatisfied);
242 
243  crm_time_get_gregorian(now, &y, &m, &d);
244  CHECK_ONE(cron_spec, "years", y);
245  CHECK_ONE(cron_spec, "months", m);
246  CHECK_ONE(cron_spec, "monthdays", d);
247 
248  crm_time_get_timeofday(now, &h, &m, &s);
249  CHECK_ONE(cron_spec, "hours", h);
250  CHECK_ONE(cron_spec, "minutes", m);
251  CHECK_ONE(cron_spec, "seconds", s);
252 
253  crm_time_get_ordinal(now, &y, &d);
254  CHECK_ONE(cron_spec, "yeardays", d);
255 
256  crm_time_get_isoweek(now, &y, &w, &d);
257  CHECK_ONE(cron_spec, "weekyears", y);
258  CHECK_ONE(cron_spec, "weeks", w);
259  CHECK_ONE(cron_spec, "weekdays", d);
260 
261  CHECK_ONE(cron_spec, "moon", phase_of_the_moon(now));
262  if (crm_element_value(cron_spec, "moon") != NULL) {
263  pcmk__config_warn("Support for 'moon' in date_spec elements "
264  "(such as %s) is deprecated and will be removed "
265  "in a future release of Pacemaker", ID(cron_spec));
266  }
267 
268  /* If we get here, either no fields were specified (which is success), or all
269  * the fields that were specified had their conditions met (which is also a
270  * success). Thus, the result is success.
271  */
272  return pcmk_rc_ok;
273 }
274 
275 static void
276 update_field(crm_time_t *t, const xmlNode *xml, const char *attr,
277  void (*time_fn)(crm_time_t *, int))
278 {
279  long long value;
280 
281  if ((pcmk__scan_ll(crm_element_value(xml, attr), &value, 0LL) == pcmk_rc_ok)
282  && (value != 0LL) && (value >= INT_MIN) && (value <= INT_MAX)) {
283  time_fn(t, (int) value);
284  }
285 }
286 
287 static crm_time_t *
288 parse_xml_duration(const crm_time_t *start, const xmlNode *duration_spec)
289 {
290  crm_time_t *end = pcmk_copy_time(start);
291 
292  update_field(end, duration_spec, "years", crm_time_add_years);
293  update_field(end, duration_spec, "months", crm_time_add_months);
294  update_field(end, duration_spec, "weeks", crm_time_add_weeks);
295  update_field(end, duration_spec, "days", crm_time_add_days);
296  update_field(end, duration_spec, "hours", crm_time_add_hours);
297  update_field(end, duration_spec, "minutes", crm_time_add_minutes);
298  update_field(end, duration_spec, "seconds", crm_time_add_seconds);
299 
300  return end;
301 }
302 
303 // Set next_change to t if t is earlier
304 static void
305 crm_time_set_if_earlier(crm_time_t *next_change, crm_time_t *t)
306 {
307  if ((next_change != NULL) && (t != NULL)) {
308  if (!crm_time_is_defined(next_change)
309  || (crm_time_compare(t, next_change) < 0)) {
310  crm_time_set(next_change, t);
311  }
312  }
313 }
314 
315 // Information about a block of nvpair elements
316 typedef struct sorted_set_s {
317  int score; // This block's score for sorting
318  const char *name; // This block's ID
319  const char *special_name; // ID that should sort first
320  xmlNode *attr_set; // This block
321  gboolean overwrite; // Whether existing values will be overwritten
322 } sorted_set_t;
323 
324 static gint
325 sort_pairs(gconstpointer a, gconstpointer b)
326 {
327  const sorted_set_t *pair_a = a;
328  const sorted_set_t *pair_b = b;
329 
330  if (a == NULL && b == NULL) {
331  return 0;
332  } else if (a == NULL) {
333  return 1;
334  } else if (b == NULL) {
335  return -1;
336  }
337 
338  if (pcmk__str_eq(pair_a->name, pair_a->special_name, pcmk__str_casei)) {
339  return -1;
340 
341  } else if (pcmk__str_eq(pair_b->name, pair_a->special_name, pcmk__str_casei)) {
342  return 1;
343  }
344 
345  /* If we're overwriting values, we want lowest score first, so the highest
346  * score is processed last; if we're not overwriting values, we want highest
347  * score first, so nothing else overwrites it.
348  */
349  if (pair_a->score < pair_b->score) {
350  return pair_a->overwrite? -1 : 1;
351  } else if (pair_a->score > pair_b->score) {
352  return pair_a->overwrite? 1 : -1;
353  }
354  return 0;
355 }
356 
357 static void
358 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
359 {
360  const char *name = NULL;
361  const char *value = NULL;
362  const char *old_value = NULL;
363  xmlNode *list = nvpair_list;
364  xmlNode *an_attr = NULL;
365 
366  if (pcmk__xe_is(list->children, XML_TAG_ATTRS)) {
367  list = list->children;
368  }
369 
370  for (an_attr = pcmk__xe_first_child(list); an_attr != NULL;
371  an_attr = pcmk__xe_next(an_attr)) {
372 
373  if (pcmk__str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, pcmk__str_none)) {
374  xmlNode *ref_nvpair = expand_idref(an_attr, top);
375 
377  if (name == NULL) {
379  }
380 
381  value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
382  if (value == NULL) {
383  value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
384  }
385 
386  if (name == NULL || value == NULL) {
387  continue;
388  }
389 
390  old_value = g_hash_table_lookup(hash, name);
391 
392  if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
393  if (old_value) {
394  crm_trace("Letting %s default (removing explicit value \"%s\")",
395  name, value);
396  g_hash_table_remove(hash, name);
397  }
398  continue;
399 
400  } else if (old_value == NULL) {
401  crm_trace("Setting %s=\"%s\"", name, value);
402  g_hash_table_insert(hash, strdup(name), strdup(value));
403 
404  } else if (overwrite) {
405  crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")",
406  name, value, old_value);
407  g_hash_table_replace(hash, strdup(name), strdup(value));
408  }
409  }
410  }
411 }
412 
413 typedef struct unpack_data_s {
414  gboolean overwrite;
415  void *hash;
416  crm_time_t *next_change;
417  const pe_rule_eval_data_t *rule_data;
418  xmlNode *top;
419 } unpack_data_t;
420 
421 static void
422 unpack_attr_set(gpointer data, gpointer user_data)
423 {
424  sorted_set_t *pair = data;
425  unpack_data_t *unpack_data = user_data;
426 
427  if (!pe_eval_rules(pair->attr_set, unpack_data->rule_data,
428  unpack_data->next_change)) {
429  return;
430  }
431 
432  crm_trace("Adding attributes from %s (score %d) %s overwrite",
433  pair->name, pair->score,
434  (unpack_data->overwrite? "with" : "without"));
435  populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
436 }
437 
449 static GList *
450 make_pairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
451  const char *always_first, gboolean overwrite)
452 {
453  GList *unsorted = NULL;
454 
455  if (xml_obj == NULL) {
456  return NULL;
457  }
458  for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj); attr_set != NULL;
459  attr_set = pcmk__xe_next(attr_set)) {
460 
461  if (pcmk__str_eq(set_name, (const char *) attr_set->name,
463  const char *score = NULL;
464  sorted_set_t *pair = NULL;
465  xmlNode *expanded_attr_set = expand_idref(attr_set, top);
466 
467  if (expanded_attr_set == NULL) {
468  // Schema (if not "none") prevents this
469  continue;
470  }
471 
472  pair = calloc(1, sizeof(sorted_set_t));
473  pair->name = ID(expanded_attr_set);
474  pair->special_name = always_first;
475  pair->attr_set = expanded_attr_set;
476  pair->overwrite = overwrite;
477 
478  score = crm_element_value(expanded_attr_set, XML_RULE_ATTR_SCORE);
479  pair->score = char2score(score);
480 
481  unsorted = g_list_prepend(unsorted, pair);
482  }
483  }
484  return g_list_sort(unsorted, sort_pairs);
485 }
486 
499 void
500 pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
501  const pe_rule_eval_data_t *rule_data, GHashTable *hash,
502  const char *always_first, gboolean overwrite,
503  crm_time_t *next_change)
504 {
505  GList *pairs = make_pairs(top, xml_obj, set_name, always_first, overwrite);
506 
507  if (pairs) {
508  unpack_data_t data = {
509  .hash = hash,
510  .overwrite = overwrite,
511  .next_change = next_change,
512  .top = top,
513  .rule_data = rule_data
514  };
515 
516  g_list_foreach(pairs, unpack_attr_set, &data);
517  g_list_free_full(pairs, free);
518  }
519 }
520 
534 void
535 pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
536  GHashTable *node_hash, GHashTable *hash,
537  const char *always_first, gboolean overwrite,
538  crm_time_t *now, crm_time_t *next_change)
539 {
540  pe_rule_eval_data_t rule_data = {
541  .node_hash = node_hash,
542  .role = pcmk_role_unknown,
543  .now = now,
544  .match_data = NULL,
545  .rsc_data = NULL,
546  .op_data = NULL
547  };
548 
549  pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash,
550  always_first, overwrite, next_change);
551 }
552 
562 char *
563 pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
564 {
565  size_t len = 0;
566  int i;
567  const char *p, *last_match_index;
568  char *p_dst, *result = NULL;
569 
570  if (pcmk__str_empty(string) || !match_data) {
571  return NULL;
572  }
573 
574  p = last_match_index = string;
575 
576  while (*p) {
577  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
578  i = *(p + 1) - '0';
579  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
580  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
581  len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
582  last_match_index = p + 2;
583  }
584  p++;
585  }
586  p++;
587  }
588  len += p - last_match_index + 1;
589 
590  /* FIXME: Excessive? */
591  if (len - 1 <= 0) {
592  return NULL;
593  }
594 
595  p_dst = result = calloc(1, len);
596  p = string;
597 
598  while (*p) {
599  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
600  i = *(p + 1) - '0';
601  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
602  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
603  /* rm_eo can be equal to rm_so, but then there is nothing to do */
604  int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
605  memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
606  p_dst += match_len;
607  }
608  p++;
609  } else {
610  *(p_dst) = *(p);
611  p_dst++;
612  }
613  p++;
614  }
615 
616  return result;
617 }
618 
628 gboolean
629 pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
630  crm_time_t *next_change)
631 {
632  // If there are no rules, pass by default
633  gboolean ruleset_default = TRUE;
634 
635  for (xmlNode *rule = first_named_child(ruleset, XML_TAG_RULE);
636  rule != NULL; rule = crm_next_same_xml(rule)) {
637 
638  ruleset_default = FALSE;
639  if (pe_eval_expr(rule, rule_data, next_change)) {
640  /* Only the deprecated "lifetime" element of location constraints
641  * may contain more than one rule at the top level -- the schema
642  * limits a block of nvpairs to a single top-level rule. So, this
643  * effectively means that a lifetime is active if any rule it
644  * contains is active.
645  */
646  return TRUE;
647  }
648  }
649 
650  return ruleset_default;
651 }
652 
662 gboolean
663 pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data,
664  crm_time_t *next_change)
665 {
666  xmlNode *expr = NULL;
667  gboolean test = TRUE;
668  gboolean empty = TRUE;
669  gboolean passed = TRUE;
670  gboolean do_and = TRUE;
671  const char *value = NULL;
672 
673  rule = expand_idref(rule, NULL);
675  if (pcmk__str_eq(value, "or", pcmk__str_casei)) {
676  do_and = FALSE;
677  passed = FALSE;
678  }
679 
680  crm_trace("Testing rule %s", ID(rule));
681  for (expr = pcmk__xe_first_child(rule); expr != NULL;
682  expr = pcmk__xe_next(expr)) {
683 
684  test = pe_eval_subexpr(expr, rule_data, next_change);
685  empty = FALSE;
686 
687  if (test && do_and == FALSE) {
688  crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
689  return TRUE;
690 
691  } else if (test == FALSE && do_and) {
692  crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
693  return FALSE;
694  }
695  }
696 
697  if (empty) {
698  crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
699  }
700 
701  crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
702  return passed;
703 }
704 
714 gboolean
715 pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data,
716  crm_time_t *next_change)
717 {
718  gboolean accept = FALSE;
719  const char *uname = NULL;
720 
721  switch (find_expression_type(expr)) {
722  case nested_rule:
723  accept = pe_eval_expr(expr, rule_data, next_change);
724  break;
725  case attr_expr:
726  case loc_expr:
727  /* these expressions can never succeed if there is
728  * no node to compare with
729  */
730  if (rule_data->node_hash != NULL) {
731  accept = pe__eval_attr_expr(expr, rule_data);
732  }
733  break;
734 
735  case time_expr:
736  switch (pe__eval_date_expr(expr, rule_data, next_change)) {
738  case pcmk_rc_ok:
739  accept = TRUE;
740  break;
741 
742  default:
743  accept = FALSE;
744  break;
745  }
746  break;
747 
748  case role_expr:
749  accept = pe__eval_role_expr(expr, rule_data);
750  break;
751 
752  case rsc_expr:
753  accept = pe__eval_rsc_expr(expr, rule_data);
754  break;
755 
756  case op_expr:
757  accept = pe__eval_op_expr(expr, rule_data);
758  break;
759 
760  default:
761  CRM_CHECK(FALSE /* bad type */ , return FALSE);
762  accept = FALSE;
763  }
764  if (rule_data->node_hash) {
765  uname = g_hash_table_lookup(rule_data->node_hash, CRM_ATTR_UNAME);
766  }
767 
768  crm_trace("Expression %s %s on %s",
769  ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
770  return accept;
771 }
772 
788 static int
789 compare_attr_expr_vals(const char *l_val, const char *r_val, const char *type,
790  const char *op)
791 {
792  int cmp = 0;
793 
794  if (l_val != NULL && r_val != NULL) {
795  if (type == NULL) {
796  if (pcmk__strcase_any_of(op, "lt", "lte", "gt", "gte", NULL)) {
797  if (pcmk__char_in_any_str('.', l_val, r_val, NULL)) {
798  type = "number";
799  } else {
800  type = "integer";
801  }
802 
803  } else {
804  type = "string";
805  }
806  crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
807  }
808 
809  if (pcmk__str_eq(type, "string", pcmk__str_casei)) {
810  cmp = strcasecmp(l_val, r_val);
811 
812  } else if (pcmk__str_eq(type, "integer", pcmk__str_casei)) {
813  long long l_val_num;
814  int rc1 = pcmk__scan_ll(l_val, &l_val_num, 0LL);
815 
816  long long r_val_num;
817  int rc2 = pcmk__scan_ll(r_val, &r_val_num, 0LL);
818 
819  if ((rc1 == pcmk_rc_ok) && (rc2 == pcmk_rc_ok)) {
820  if (l_val_num < r_val_num) {
821  cmp = -1;
822  } else if (l_val_num > r_val_num) {
823  cmp = 1;
824  } else {
825  cmp = 0;
826  }
827 
828  } else {
829  crm_debug("Integer parse error. Comparing %s and %s as strings",
830  l_val, r_val);
831  cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
832  }
833 
834  } else if (pcmk__str_eq(type, "number", pcmk__str_casei)) {
835  double l_val_num;
836  double r_val_num;
837 
838  int rc1 = pcmk__scan_double(l_val, &l_val_num, NULL, NULL);
839  int rc2 = pcmk__scan_double(r_val, &r_val_num, NULL, NULL);
840 
841  if (rc1 == pcmk_rc_ok && rc2 == pcmk_rc_ok) {
842  if (l_val_num < r_val_num) {
843  cmp = -1;
844  } else if (l_val_num > r_val_num) {
845  cmp = 1;
846  } else {
847  cmp = 0;
848  }
849 
850  } else {
851  crm_debug("Floating-point parse error. Comparing %s and %s as "
852  "strings", l_val, r_val);
853  cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
854  }
855 
856  } else if (pcmk__str_eq(type, "version", pcmk__str_casei)) {
857  cmp = compare_version(l_val, r_val);
858 
859  }
860 
861  } else if (l_val == NULL && r_val == NULL) {
862  cmp = 0;
863  } else if (r_val == NULL) {
864  cmp = 1;
865  } else { // l_val == NULL && r_val != NULL
866  cmp = -1;
867  }
868 
869  return cmp;
870 }
871 
886 static bool
887 accept_attr_expr(const char *l_val, const char *r_val, const char *type,
888  const char *op)
889 {
890  int cmp;
891 
892  if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
893  return (l_val != NULL);
894 
895  } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
896  return (l_val == NULL);
897 
898  }
899 
900  cmp = compare_attr_expr_vals(l_val, r_val, type, op);
901 
902  if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
903  return (cmp == 0);
904 
905  } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
906  return (cmp != 0);
907 
908  } else if (l_val == NULL || r_val == NULL) {
909  // The comparison is meaningless from this point on
910  return false;
911 
912  } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
913  return (cmp < 0);
914 
915  } else if (pcmk__str_eq(op, "lte", pcmk__str_casei)) {
916  return (cmp <= 0);
917 
918  } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
919  return (cmp > 0);
920 
921  } else if (pcmk__str_eq(op, "gte", pcmk__str_casei)) {
922  return (cmp >= 0);
923  }
924 
925  return false; // Should never reach this point
926 }
927 
936 static const char *
937 expand_value_source(const char *value, const char *value_source,
938  const pe_match_data_t *match_data)
939 {
940  GHashTable *table = NULL;
941 
942  if (pcmk__str_empty(value)) {
943  return NULL; // value_source is irrelevant
944 
945  } else if (pcmk__str_eq(value_source, "param", pcmk__str_casei)) {
946  table = match_data->params;
947 
948  } else if (pcmk__str_eq(value_source, "meta", pcmk__str_casei)) {
949  table = match_data->meta;
950 
951  } else { // literal
952  return value;
953  }
954 
955  if (table == NULL) {
956  return NULL;
957  }
958  return (const char *) g_hash_table_lookup(table, value);
959 }
960 
971 gboolean
972 pe__eval_attr_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
973 {
974  gboolean attr_allocated = FALSE;
975  const char *h_val = NULL;
976 
977  const char *op = NULL;
978  const char *type = NULL;
979  const char *attr = NULL;
980  const char *value = NULL;
981  const char *value_source = NULL;
982 
985  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
987  value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
988 
989  if (attr == NULL) {
990  pe_err("Expression %s invalid: " XML_EXPR_ATTR_ATTRIBUTE
991  " not specified", pcmk__s(ID(expr), "without ID"));
992  return FALSE;
993  } else if (op == NULL) {
994  pe_err("Expression %s invalid: " XML_EXPR_ATTR_OPERATION
995  " not specified", pcmk__s(ID(expr), "without ID"));
996  }
997 
998  if (rule_data->match_data != NULL) {
999  // Expand any regular expression submatches (%0-%9) in attribute name
1000  if (rule_data->match_data->re != NULL) {
1001  char *resolved_attr = pe_expand_re_matches(attr, rule_data->match_data->re);
1002 
1003  if (resolved_attr != NULL) {
1004  attr = (const char *) resolved_attr;
1005  attr_allocated = TRUE;
1006  }
1007  }
1008 
1009  // Get value appropriate to value-source
1010  value = expand_value_source(value, value_source, rule_data->match_data);
1011  }
1012 
1013  if (rule_data->node_hash != NULL) {
1014  h_val = (const char *)g_hash_table_lookup(rule_data->node_hash, attr);
1015  }
1016 
1017  if (attr_allocated) {
1018  free((char *)attr);
1019  attr = NULL;
1020  }
1021 
1022  return accept_attr_expr(h_val, value, type, op);
1023 }
1024 
1035 int
1036 pe__eval_date_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data,
1037  crm_time_t *next_change)
1038 {
1039  crm_time_t *start = NULL;
1040  crm_time_t *end = NULL;
1041  const char *value = NULL;
1042  const char *op = crm_element_value(expr, "operation");
1043 
1044  xmlNode *duration_spec = NULL;
1045  xmlNode *date_spec = NULL;
1046 
1047  // "undetermined" will also be returned for parsing errors
1048  int rc = pcmk_rc_undetermined;
1049 
1050  crm_trace("Testing expression: %s", ID(expr));
1051 
1052  duration_spec = first_named_child(expr, "duration");
1053  date_spec = first_named_child(expr, "date_spec");
1054 
1055  value = crm_element_value(expr, "start");
1056  if (value != NULL) {
1057  start = crm_time_new(value);
1058  }
1059  value = crm_element_value(expr, "end");
1060  if (value != NULL) {
1061  end = crm_time_new(value);
1062  }
1063 
1064  if (start != NULL && end == NULL && duration_spec != NULL) {
1065  end = parse_xml_duration(start, duration_spec);
1066  }
1067 
1068  if (pcmk__str_eq(op, "in_range", pcmk__str_null_matches | pcmk__str_casei)) {
1069  if ((start == NULL) && (end == NULL)) {
1070  // in_range requires at least one of start or end
1071  } else if ((start != NULL) && (crm_time_compare(rule_data->now, start) < 0)) {
1072  rc = pcmk_rc_before_range;
1073  crm_time_set_if_earlier(next_change, start);
1074  } else if ((end != NULL) && (crm_time_compare(rule_data->now, end) > 0)) {
1075  rc = pcmk_rc_after_range;
1076  } else {
1077  rc = pcmk_rc_within_range;
1078  if (end && next_change) {
1079  // Evaluation doesn't change until second after end
1080  crm_time_add_seconds(end, 1);
1081  crm_time_set_if_earlier(next_change, end);
1082  }
1083  }
1084 
1085  } else if (pcmk__str_eq(op, "date_spec", pcmk__str_casei)) {
1086  rc = pe_cron_range_satisfied(rule_data->now, date_spec);
1087  // @TODO set next_change appropriately
1088 
1089  } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
1090  if (start == NULL) {
1091  // gt requires start
1092  } else if (crm_time_compare(rule_data->now, start) > 0) {
1093  rc = pcmk_rc_within_range;
1094  } else {
1095  rc = pcmk_rc_before_range;
1096 
1097  // Evaluation doesn't change until second after start
1098  crm_time_add_seconds(start, 1);
1099  crm_time_set_if_earlier(next_change, start);
1100  }
1101 
1102  } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
1103  if (end == NULL) {
1104  // lt requires end
1105  } else if (crm_time_compare(rule_data->now, end) < 0) {
1106  rc = pcmk_rc_within_range;
1107  crm_time_set_if_earlier(next_change, end);
1108  } else {
1109  rc = pcmk_rc_after_range;
1110  }
1111  }
1112 
1113  crm_time_free(start);
1114  crm_time_free(end);
1115  return rc;
1116 }
1117 
1118 gboolean
1119 pe__eval_op_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
1120 {
1121  const char *name = crm_element_value(expr, XML_NVPAIR_ATTR_NAME);
1122  const char *interval_s = crm_element_value(expr, XML_LRM_ATTR_INTERVAL);
1123  guint interval;
1124 
1125  crm_trace("Testing op_defaults expression: %s", ID(expr));
1126 
1127  if (rule_data->op_data == NULL) {
1128  crm_trace("No operations data provided");
1129  return FALSE;
1130  }
1131 
1132  interval = crm_parse_interval_spec(interval_s);
1133  if (interval == 0 && errno != 0) {
1134  crm_trace("Could not parse interval: %s", interval_s);
1135  return FALSE;
1136  }
1137 
1138  if (interval_s != NULL && interval != rule_data->op_data->interval) {
1139  crm_trace("Interval doesn't match: %d != %d", interval, rule_data->op_data->interval);
1140  return FALSE;
1141  }
1142 
1143  if (!pcmk__str_eq(name, rule_data->op_data->op_name, pcmk__str_none)) {
1144  crm_trace("Name doesn't match: %s != %s", name, rule_data->op_data->op_name);
1145  return FALSE;
1146  }
1147 
1148  return TRUE;
1149 }
1150 
1160 gboolean
1161 pe__eval_role_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
1162 {
1163  gboolean accept = FALSE;
1164  const char *op = NULL;
1165  const char *value = NULL;
1166 
1167  if (rule_data->role == pcmk_role_unknown) {
1168  return accept;
1169  }
1170 
1171  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
1173 
1174  if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
1175  if (rule_data->role > pcmk_role_started) {
1176  accept = TRUE;
1177  }
1178 
1179  } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
1180  if ((rule_data->role > pcmk_role_unknown)
1181  && (rule_data->role < pcmk_role_unpromoted)) {
1182  accept = TRUE;
1183  }
1184 
1185  } else if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
1186  if (text2role(value) == rule_data->role) {
1187  accept = TRUE;
1188  }
1189 
1190  } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
1191  // Test "ne" only with promotable clone roles
1192  if ((rule_data->role > pcmk_role_unknown)
1193  && (rule_data->role < pcmk_role_unpromoted)) {
1194  accept = FALSE;
1195 
1196  } else if (text2role(value) != rule_data->role) {
1197  accept = TRUE;
1198  }
1199  }
1200  return accept;
1201 }
1202 
1203 gboolean
1204 pe__eval_rsc_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
1205 {
1206  const char *class = crm_element_value(expr, XML_AGENT_ATTR_CLASS);
1207  const char *provider = crm_element_value(expr, XML_AGENT_ATTR_PROVIDER);
1208  const char *type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
1209 
1210  crm_trace("Testing rsc_defaults expression: %s", ID(expr));
1211 
1212  if (rule_data->rsc_data == NULL) {
1213  crm_trace("No resource data provided");
1214  return FALSE;
1215  }
1216 
1217  if (class != NULL &&
1218  !pcmk__str_eq(class, rule_data->rsc_data->standard, pcmk__str_none)) {
1219  crm_trace("Class doesn't match: %s != %s", class, rule_data->rsc_data->standard);
1220  return FALSE;
1221  }
1222 
1223  if ((provider == NULL && rule_data->rsc_data->provider != NULL) ||
1224  (provider != NULL && rule_data->rsc_data->provider == NULL) ||
1225  !pcmk__str_eq(provider, rule_data->rsc_data->provider, pcmk__str_none)) {
1226  crm_trace("Provider doesn't match: %s != %s", provider, rule_data->rsc_data->provider);
1227  return FALSE;
1228  }
1229 
1230  if (type != NULL &&
1231  !pcmk__str_eq(type, rule_data->rsc_data->agent, pcmk__str_none)) {
1232  crm_trace("Agent doesn't match: %s != %s", type, rule_data->rsc_data->agent);
1233  return FALSE;
1234  }
1235 
1236  return TRUE;
1237 }
1238 
1239 // Deprecated functions kept only for backward API compatibility
1240 // LCOV_EXCL_START
1241 
1242 #include <crm/pengine/rules_compat.h>
1243 
1244 gboolean
1245 test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
1246 {
1247  return pe_evaluate_rules(ruleset, node_hash, now, NULL);
1248 }
1249 
1250 gboolean
1251 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1252 {
1253  return pe_test_rule(rule, node_hash, role, now, NULL, NULL);
1254 }
1255 
1256 gboolean
1257 pe_test_rule_re(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
1258 {
1259  pe_match_data_t match_data = {
1260  .re = re_match_data,
1261  .params = NULL,
1262  .meta = NULL,
1263  };
1264  return pe_test_rule(rule, node_hash, role, now, NULL, &match_data);
1265 }
1266 
1267 gboolean
1268 pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
1269  crm_time_t *now, pe_match_data_t *match_data)
1270 {
1271  return pe_test_rule(rule, node_hash, role, now, NULL, match_data);
1272 }
1273 
1274 gboolean
1275 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1276 {
1277  return pe_test_expression(expr, node_hash, role, now, NULL, NULL);
1278 }
1279 
1280 gboolean
1281 pe_test_expression_re(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_re_match_data_t * re_match_data)
1282 {
1283  pe_match_data_t match_data = {
1284  .re = re_match_data,
1285  .params = NULL,
1286  .meta = NULL,
1287  };
1288  return pe_test_expression(expr, node_hash, role, now, NULL, &match_data);
1289 }
1290 
1291 gboolean
1292 pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
1293  enum rsc_role_e role, crm_time_t *now,
1294  pe_match_data_t *match_data)
1295 {
1296  return pe_test_expression(expr, node_hash, role, now, NULL, match_data);
1297 }
1298 
1299 void
1300 unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
1301  GHashTable *node_hash, GHashTable *hash,
1302  const char *always_first, gboolean overwrite,
1303  crm_time_t *now)
1304 {
1305  pe_rule_eval_data_t rule_data = {
1306  .node_hash = node_hash,
1307  .role = pcmk_role_unknown,
1308  .now = now,
1309  .match_data = NULL,
1310  .rsc_data = NULL,
1311  .op_data = NULL
1312  };
1313 
1314  pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash, always_first,
1315  overwrite, NULL);
1316 }
1317 
1318 // LCOV_EXCL_STOP
1319 // End deprecated API
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
int pe__eval_date_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:1036
void pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, const pe_rule_eval_data_t *rule_data, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition: rules.c:500
A dumping ground.
void crm_time_add_years(crm_time_t *dt, int value)
Definition: iso8601.c:1654
gboolean pe_test_rule_re(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_re_match_data_t *re_match_data)
Definition: rules.c:1257
const char * provider
Definition: common.h:70
int crm_time_get_isoweek(const crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
Definition: iso8601.c:406
Definition: rules.h:33
Deprecated Pacemaker rule API.
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:1548
char data[0]
Definition: cpg.c:55
bool pcmk__char_in_any_str(int ch,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:980
#define CRM_ATTR_KIND
Definition: crm.h:115
#define XML_EXPR_ATTR_TYPE
Definition: msg_xml.h:350
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition: iso8601.c:142
const char * name
Definition: cib.c:26
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:933
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define pcmk__config_warn(fmt...)
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1275
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:341
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
crm_time_t * pcmk_copy_time(const crm_time_t *source)
Definition: iso8601.c:1374
pe_re_match_data_t * re
Definition: common.h:63
#define XML_TAG_ATTRS
Definition: msg_xml.h:229
gboolean pe_test_expression_re(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_re_match_data_t *re_match_data)
Definition: rules.c:1281
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:300
#define CRM_ATTR_ROLE
Definition: crm.h:116
#define XML_EXPR_ATTR_VALUE_SOURCE
Definition: msg_xml.h:351
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:393
#define CHECK_ONE(spec, name, var)
Definition: rules.c:229
enum crm_ais_msg_types type
Definition: cpg.c:48
gboolean pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, crm_time_t *next_change, pe_match_data_t *match_data)
Definition: rules.c:55
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:224
Definition: rules.h:34
int crm_time_get_ordinal(const crm_time_t *dt, uint32_t *y, uint32_t *d)
Definition: iso8601.c:398
void crm_time_add_hours(crm_time_t *dt, int value)
Definition: iso8601.c:1642
gboolean pe_test_expression_full(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_match_data_t *match_data)
Definition: rules.c:1292
guint interval
Definition: common.h:76
pe_match_data_t * match_data
Definition: common.h:83
int pcmk__scan_ll(const char *text, long long *result, long long default_value)
Definition: strings.c:97
gboolean pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, crm_time_t *next_change, pe_match_data_t *match_data)
Evaluate one rule subelement (pass/fail)
Definition: rules.c:88
pe_op_eval_data_t * op_data
Definition: common.h:85
void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition: rules.c:535
Definition: rules.h:23
#define crm_debug(fmt, args...)
Definition: logging.h:386
void crm_time_set(crm_time_t *target, const crm_time_t *source)
Definition: iso8601.c:1305
void crm_time_add_weeks(crm_time_t *dt, int value)
Definition: iso8601.c:1648
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
#define XML_EXPR_ATTR_VALUE
Definition: msg_xml.h:349
void crm_time_add_months(crm_time_t *dt, int value)
Definition: iso8601.c:1593
#define CRM_ATTR_UNAME
Definition: crm.h:113
#define crm_trace(fmt, args...)
Definition: logging.h:387
gboolean pe__eval_attr_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition: rules.c:972
gboolean pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Evaluate a single rule expression, including any subexpressions.
Definition: rules.c:715
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2555
gboolean pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Evaluate all of a rule&#39;s expressions.
Definition: rules.c:663
const char * op_name
Definition: common.h:75
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:281
Unpromoted.
Definition: roles.h:31
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:27
int pe_cron_range_satisfied(const crm_time_t *now, const xmlNode *cron_spec)
Definition: rules.c:237
void crm_time_add_minutes(crm_time_t *dt, int value)
Definition: iso8601.c:1636
#define XML_EXPR_ATTR_OPERATION
Definition: msg_xml.h:348
GHashTable * meta
Definition: common.h:65
gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, crm_time_t *next_change)
Evaluate any rules contained by given XML element.
Definition: rules.c:39
gboolean pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now, pe_match_data_t *match_data)
Definition: rules.c:1268
int crm_time_get_gregorian(const crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
Definition: iso8601.c:365
enum rsc_role_e text2role(const char *role)
Definition: common.c:487
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:957
int pcmk__scan_double(const char *text, double *result, const char *default_text, char **end_text)
Definition: strings.c:199
pe_rsc_eval_data_t * rsc_data
Definition: common.h:84
int crm_time_compare(const crm_time_t *a, const crm_time_t *b)
Definition: iso8601.c:1518
const char * agent
Definition: common.h:71
#define XML_RULE_ATTR_BOOLEAN_OP
Definition: msg_xml.h:344
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1251
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
struct unpack_data_s unpack_data_t
CRM_TRACE_INIT_DATA(pe_rules)
GHashTable * params
Definition: common.h:64
struct sorted_set_s sorted_set_t
const char * standard
Definition: common.h:69
pcmk__action_result_t result
Definition: pcmk_fence.c:35
#define PCMK_XE_DATE_EXPRESSION
Definition: msg_xml.h:36
crm_time_t * now
Definition: common.h:82
#define crm_err(fmt, args...)
Definition: logging.h:381
GHashTable * node_hash
Definition: common.h:80
char guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:271
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition: rules.c:1245
gboolean pe__eval_op_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition: rules.c:1119
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:109
regmatch_t * pmatch
Definition: common.h:59
Started.
Definition: roles.h:30
int compare_version(const char *version1, const char *version2)
Definition: utils.c:189
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:394
char uname[MAX_NAME]
Definition: cpg.c:50
gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Evaluate rules.
Definition: rules.c:629
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:810
Definition: rules.h:26
gboolean pe__eval_role_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition: rules.c:1161
expression_type
Definition: rules.h:22
#define PCMK_XE_OP_EXPRESSION
Definition: msg_xml.h:37
Resource role is unknown.
Definition: roles.h:28
#define XML_TAG_EXPRESSION
Definition: msg_xml.h:346
gboolean pe__eval_rsc_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data)
Definition: rules.c:1204
#define ID(x)
Definition: msg_xml.h:474
#define PCMK_XE_RSC_EXPRESSION
Definition: msg_xml.h:44
void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now)
Definition: rules.c:1300
#define pe_err(fmt...)
Definition: internal.h:39
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:105
#define XML_TAG_RULE
Definition: msg_xml.h:340
enum rsc_role_e role
Definition: common.h:81
#define CRM_ATTR_ID
Definition: crm.h:114
int crm_time_get_timeofday(const crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
Definition: iso8601.c:299
char * pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
Expand any regular expression submatches (%0-%9) in a string.
Definition: rules.c:563
#define XML_EXPR_ATTR_ATTRIBUTE
Definition: msg_xml.h:347
void crm_time_add_days(crm_time_t *dt, int value)
Definition: iso8601.c:1568
char * string
Definition: common.h:57
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:280
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2510
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:150