pacemaker  2.1.4-dc6eb4362
Scalable High-Availability cluster resource manager
rules.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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 = RSC_ROLE_UNKNOWN,
45  .now = now,
46  .match_data = NULL,
47  .rsc_data = NULL,
48  .op_data = NULL
49  };
50 
51  return pe_eval_rules(ruleset, &rule_data, next_change);
52 }
53 
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 *tag = NULL;
108  const char *attr = NULL;
109 
111  tag = crm_element_name(expr);
112 
113  if (pcmk__str_eq(tag, "date_expression", pcmk__str_casei)) {
114  return time_expr;
115 
116  } else if (pcmk__str_eq(tag, "rsc_expression", pcmk__str_casei)) {
117  return rsc_expr;
118 
119  } else if (pcmk__str_eq(tag, "op_expression", pcmk__str_casei)) {
120  return op_expr;
121 
122  } else if (pcmk__str_eq(tag, XML_TAG_RULE, pcmk__str_casei)) {
123  return nested_rule;
124 
125  } else if (!pcmk__str_eq(tag, "expression", pcmk__str_casei)) {
126  return not_expr;
127 
128  } else if (pcmk__strcase_any_of(attr, CRM_ATTR_UNAME, CRM_ATTR_KIND, CRM_ATTR_ID, NULL)) {
129  return loc_expr;
130 
131  } else if (pcmk__str_eq(attr, CRM_ATTR_ROLE, pcmk__str_casei)) {
132  return role_expr;
133 
134 #if ENABLE_VERSIONED_ATTRS
135  } else if (pcmk__str_eq(attr, CRM_ATTR_RA_VERSION, pcmk__str_casei)) {
136  return version_expr;
137 #endif
138  }
139 
140  return attr_expr;
141 }
142 
143 /* As per the nethack rules:
144  *
145  * moon period = 29.53058 days ~= 30, year = 365.2422 days
146  * days moon phase advances on first day of year compared to preceding year
147  * = 365.2422 - 12*29.53058 ~= 11
148  * years in Metonic cycle (time until same phases fall on the same days of
149  * the month) = 18.6 ~= 19
150  * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
151  * (29 as initial condition)
152  * current phase in days = first day phase + days elapsed in year
153  * 6 moons ~= 177 days
154  * 177 ~= 8 reported phases * 22
155  * + 11/22 for rounding
156  *
157  * 0-7, with 0: new, 4: full
158  */
159 
160 static int
161 phase_of_the_moon(crm_time_t * now)
162 {
163  uint32_t epact, diy, goldn;
164  uint32_t y;
165 
166  crm_time_get_ordinal(now, &y, &diy);
167 
168  goldn = (y % 19) + 1;
169  epact = (11 * goldn + 18) % 30;
170  if ((epact == 25 && goldn > 11) || epact == 24)
171  epact++;
172 
173  return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
174 }
175 
176 static int
177 check_one(xmlNode *cron_spec, const char *xml_field, uint32_t time_field) {
178  int rc = pcmk_rc_undetermined;
179  const char *value = crm_element_value(cron_spec, xml_field);
180  long long low, high;
181 
182  if (value == NULL) {
183  /* Return pe_date_result_undetermined if the field is missing. */
184  goto bail;
185  }
186 
187  if (pcmk__parse_ll_range(value, &low, &high) == pcmk_rc_unknown_format) {
188  goto bail;
189  } else if (low == high) {
190  /* A single number was given, not a range. */
191  if (time_field < low) {
193  } else if (time_field > high) {
194  rc = pcmk_rc_after_range;
195  } else {
197  }
198  } else if (low != -1 && high != -1) {
199  /* This is a range with both bounds. */
200  if (time_field < low) {
202  } else if (time_field > high) {
203  rc = pcmk_rc_after_range;
204  } else {
206  }
207  } else if (low == -1) {
208  /* This is a range with no starting value. */
209  rc = time_field <= high ? pcmk_rc_within_range : pcmk_rc_after_range;
210  } else if (high == -1) {
211  /* This is a range with no ending value. */
212  rc = time_field >= low ? pcmk_rc_within_range : pcmk_rc_before_range;
213  }
214 
215 bail:
216  if (rc == pcmk_rc_within_range) {
217  crm_debug("Condition '%s' in %s: passed", value, xml_field);
218  } else {
219  crm_debug("Condition '%s' in %s: failed", value, xml_field);
220  }
221 
222  return rc;
223 }
224 
225 static gboolean
226 check_passes(int rc) {
227  /* _within_range is obvious. _undetermined is a pass because
228  * this is the return value if a field is not given. In this
229  * case, we just want to ignore it and check other fields to
230  * see if they place some restriction on what can pass.
231  */
232  return rc == pcmk_rc_within_range || rc == pcmk_rc_undetermined;
233 }
234 
235 #define CHECK_ONE(spec, name, var) do { \
236  int subpart_rc = check_one(spec, name, var); \
237  if (check_passes(subpart_rc) == FALSE) { \
238  return subpart_rc; \
239  } \
240 } while (0)
241 
242 int
243 pe_cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec)
244 {
245  uint32_t h, m, s, y, d, w;
246 
247  CRM_CHECK(now != NULL, return pcmk_rc_op_unsatisfied);
248 
249  crm_time_get_gregorian(now, &y, &m, &d);
250  CHECK_ONE(cron_spec, "years", y);
251  CHECK_ONE(cron_spec, "months", m);
252  CHECK_ONE(cron_spec, "monthdays", d);
253 
254  crm_time_get_timeofday(now, &h, &m, &s);
255  CHECK_ONE(cron_spec, "hours", h);
256  CHECK_ONE(cron_spec, "minutes", m);
257  CHECK_ONE(cron_spec, "seconds", s);
258 
259  crm_time_get_ordinal(now, &y, &d);
260  CHECK_ONE(cron_spec, "yeardays", d);
261 
262  crm_time_get_isoweek(now, &y, &w, &d);
263  CHECK_ONE(cron_spec, "weekyears", y);
264  CHECK_ONE(cron_spec, "weeks", w);
265  CHECK_ONE(cron_spec, "weekdays", d);
266 
267  CHECK_ONE(cron_spec, "moon", phase_of_the_moon(now));
268 
269  /* If we get here, either no fields were specified (which is success), or all
270  * the fields that were specified had their conditions met (which is also a
271  * success). Thus, the result is success.
272  */
273  return pcmk_rc_ok;
274 }
275 
276 static void
277 update_field(crm_time_t *t, xmlNode *xml, const char *attr,
278  void (*time_fn)(crm_time_t *, int))
279 {
280  long long value;
281 
282  if ((pcmk__scan_ll(crm_element_value(xml, attr), &value, 0LL) == pcmk_rc_ok)
283  && (value != 0LL) && (value >= INT_MIN) && (value <= INT_MAX)) {
284  time_fn(t, (int) value);
285  }
286 }
287 
288 crm_time_t *
289 pe_parse_xml_duration(crm_time_t * start, xmlNode * duration_spec)
290 {
292 
293  crm_time_set(end, start);
294 
295  update_field(end, duration_spec, "years", crm_time_add_years);
296  update_field(end, duration_spec, "months", crm_time_add_months);
297  update_field(end, duration_spec, "weeks", crm_time_add_weeks);
298  update_field(end, duration_spec, "days", crm_time_add_days);
299  update_field(end, duration_spec, "hours", crm_time_add_hours);
300  update_field(end, duration_spec, "minutes", crm_time_add_minutes);
301  update_field(end, duration_spec, "seconds", crm_time_add_seconds);
302 
303  return end;
304 }
305 
306 // Set next_change to t if t is earlier
307 static void
308 crm_time_set_if_earlier(crm_time_t *next_change, crm_time_t *t)
309 {
310  if ((next_change != NULL) && (t != NULL)) {
311  if (!crm_time_is_defined(next_change)
312  || (crm_time_compare(t, next_change) < 0)) {
313  crm_time_set(next_change, t);
314  }
315  }
316 }
317 
318 // Information about a block of nvpair elements
319 typedef struct sorted_set_s {
320  int score; // This block's score for sorting
321  const char *name; // This block's ID
322  const char *special_name; // ID that should sort first
323  xmlNode *attr_set; // This block
324 } sorted_set_t;
325 
326 static gint
327 sort_pairs(gconstpointer a, gconstpointer b)
328 {
329  const sorted_set_t *pair_a = a;
330  const sorted_set_t *pair_b = b;
331 
332  if (a == NULL && b == NULL) {
333  return 0;
334  } else if (a == NULL) {
335  return 1;
336  } else if (b == NULL) {
337  return -1;
338  }
339 
340  if (pcmk__str_eq(pair_a->name, pair_a->special_name, pcmk__str_casei)) {
341  return -1;
342 
343  } else if (pcmk__str_eq(pair_b->name, pair_a->special_name, pcmk__str_casei)) {
344  return 1;
345  }
346 
347  if (pair_a->score < pair_b->score) {
348  return 1;
349  } else if (pair_a->score > pair_b->score) {
350  return -1;
351  }
352  return 0;
353 }
354 
355 static void
356 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
357 {
358  const char *name = NULL;
359  const char *value = NULL;
360  const char *old_value = NULL;
361  xmlNode *list = nvpair_list;
362  xmlNode *an_attr = NULL;
363 
364  name = crm_element_name(list->children);
365  if (pcmk__str_eq(XML_TAG_ATTRS, name, pcmk__str_casei)) {
366  list = list->children;
367  }
368 
369  for (an_attr = pcmk__xe_first_child(list); an_attr != NULL;
370  an_attr = pcmk__xe_next(an_attr)) {
371 
372  if (pcmk__str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, pcmk__str_none)) {
373  xmlNode *ref_nvpair = expand_idref(an_attr, top);
374 
376  if (name == NULL) {
378  }
379 
380  value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
381  if (value == NULL) {
382  value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
383  }
384 
385  if (name == NULL || value == NULL) {
386  continue;
387  }
388 
389  old_value = g_hash_table_lookup(hash, name);
390 
391  if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
392  if (old_value) {
393  crm_trace("Letting %s default (removing explicit value \"%s\")",
394  name, value);
395  g_hash_table_remove(hash, name);
396  }
397  continue;
398 
399  } else if (old_value == NULL) {
400  crm_trace("Setting %s=\"%s\"", name, value);
401  g_hash_table_insert(hash, strdup(name), strdup(value));
402 
403  } else if (overwrite) {
404  crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")",
405  name, value, old_value);
406  g_hash_table_replace(hash, strdup(name), strdup(value));
407  }
408  }
409  }
410 }
411 
412 #if ENABLE_VERSIONED_ATTRS
413 static xmlNode*
414 get_versioned_rule(xmlNode * attr_set)
415 {
416  xmlNode * rule = NULL;
417  xmlNode * expr = NULL;
418 
419  for (rule = pcmk__xe_first_child(attr_set); rule != NULL;
420  rule = pcmk__xe_next(rule)) {
421 
422  if (pcmk__str_eq((const char *)rule->name, XML_TAG_RULE,
423  pcmk__str_none)) {
424  for (expr = pcmk__xe_first_child(rule); expr != NULL;
425  expr = pcmk__xe_next(expr)) {
426 
427  if (find_expression_type(expr) == version_expr) {
428  return rule;
429  }
430  }
431  }
432  }
433 
434  return NULL;
435 }
436 
437 static void
438 add_versioned_attributes(xmlNode * attr_set, xmlNode * versioned_attrs)
439 {
440  xmlNode *attr_set_copy = NULL;
441  xmlNode *rule = NULL;
442  xmlNode *expr = NULL;
443 
444  if (!attr_set || !versioned_attrs) {
445  return;
446  }
447 
448  attr_set_copy = copy_xml(attr_set);
449 
450  rule = get_versioned_rule(attr_set_copy);
451  if (!rule) {
452  free_xml(attr_set_copy);
453  return;
454  }
455 
456  expr = pcmk__xe_first_child(rule);
457  while (expr != NULL) {
458  if (find_expression_type(expr) != version_expr) {
459  xmlNode *node = expr;
460 
461  expr = pcmk__xe_next(expr);
462  free_xml(node);
463  } else {
464  expr = pcmk__xe_next(expr);
465  }
466  }
467 
468  add_node_nocopy(versioned_attrs, NULL, attr_set_copy);
469 }
470 #endif
471 
472 typedef struct unpack_data_s {
473  gboolean overwrite;
474  void *hash;
475  crm_time_t *next_change;
476  pe_rule_eval_data_t *rule_data;
477  xmlNode *top;
478 } unpack_data_t;
479 
480 static void
481 unpack_attr_set(gpointer data, gpointer user_data)
482 {
483  sorted_set_t *pair = data;
484  unpack_data_t *unpack_data = user_data;
485 
486  if (!pe_eval_rules(pair->attr_set, unpack_data->rule_data,
487  unpack_data->next_change)) {
488  return;
489  }
490 
491 #if ENABLE_VERSIONED_ATTRS
492  if (get_versioned_rule(pair->attr_set) && !(unpack_data->rule_data->node_hash &&
493  g_hash_table_lookup_extended(unpack_data->rule_data->node_hash,
494  CRM_ATTR_RA_VERSION, NULL, NULL))) {
495  // we haven't actually tested versioned expressions yet
496  return;
497  }
498 #endif
499 
500  crm_trace("Adding attributes from %s (score %d) %s overwrite",
501  pair->name, pair->score,
502  (unpack_data->overwrite? "with" : "without"));
503  populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
504 }
505 
506 #if ENABLE_VERSIONED_ATTRS
507 static void
508 unpack_versioned_attr_set(gpointer data, gpointer user_data)
509 {
510  sorted_set_t *pair = data;
511  unpack_data_t *unpack_data = user_data;
512 
513  if (pe_eval_rules(pair->attr_set, unpack_data->rule_data,
514  unpack_data->next_change)) {
515  add_versioned_attributes(pair->attr_set, unpack_data->hash);
516  }
517 }
518 #endif
519 
531 static GList *
532 make_pairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
533  const char *always_first)
534 {
535  GList *unsorted = NULL;
536 
537  if (xml_obj == NULL) {
538  return NULL;
539  }
540  for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj); attr_set != NULL;
541  attr_set = pcmk__xe_next(attr_set)) {
542 
543  if (pcmk__str_eq(set_name, (const char *) attr_set->name,
545  const char *score = NULL;
546  sorted_set_t *pair = NULL;
547  xmlNode *expanded_attr_set = expand_idref(attr_set, top);
548 
549  if (expanded_attr_set == NULL) {
550  // Schema (if not "none") prevents this
551  continue;
552  }
553 
554  pair = calloc(1, sizeof(sorted_set_t));
555  pair->name = ID(expanded_attr_set);
556  pair->special_name = always_first;
557  pair->attr_set = expanded_attr_set;
558 
559  score = crm_element_value(expanded_attr_set, XML_RULE_ATTR_SCORE);
560  pair->score = char2score(score);
561 
562  unsorted = g_list_prepend(unsorted, pair);
563  }
564  }
565  return g_list_sort(unsorted, sort_pairs);
566 }
567 
582 static void
583 unpack_nvpair_blocks(xmlNode *top, xmlNode *xml_obj, const char *set_name,
584  void *hash, const char *always_first, gboolean overwrite,
585  pe_rule_eval_data_t *rule_data, crm_time_t *next_change,
586  GFunc unpack_func)
587 {
588  GList *pairs = make_pairs(top, xml_obj, set_name, always_first);
589 
590  if (pairs) {
591  unpack_data_t data = {
592  .hash = hash,
593  .overwrite = overwrite,
594  .next_change = next_change,
595  .top = top,
596  .rule_data = rule_data
597  };
598 
599  g_list_foreach(pairs, unpack_func, &data);
600  g_list_free_full(pairs, free);
601  }
602 }
603 
604 void
605 pe_eval_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
606  pe_rule_eval_data_t *rule_data, GHashTable *hash,
607  const char *always_first, gboolean overwrite,
608  crm_time_t *next_change)
609 {
610  unpack_nvpair_blocks(top, xml_obj, set_name, hash, always_first,
611  overwrite, rule_data, next_change, unpack_attr_set);
612 }
613 
627 void
628 pe_unpack_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
629  GHashTable *node_hash, GHashTable *hash,
630  const char *always_first, gboolean overwrite,
631  crm_time_t *now, crm_time_t *next_change)
632 {
633  pe_rule_eval_data_t rule_data = {
634  .node_hash = node_hash,
635  .role = RSC_ROLE_UNKNOWN,
636  .now = now,
637  .match_data = NULL,
638  .rsc_data = NULL,
639  .op_data = NULL
640  };
641 
642  pe_eval_nvpairs(top, xml_obj, set_name, &rule_data, hash,
643  always_first, overwrite, next_change);
644 }
645 
646 #if ENABLE_VERSIONED_ATTRS
647 void
648 pe_eval_versioned_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
649  pe_rule_eval_data_t *rule_data, xmlNode *hash,
650  crm_time_t *next_change)
651 {
652  unpack_nvpair_blocks(top, xml_obj, set_name, hash, NULL, FALSE, rule_data,
653  next_change, unpack_versioned_attr_set);
654 }
655 #endif
656 
657 char *
658 pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
659 {
660  size_t len = 0;
661  int i;
662  const char *p, *last_match_index;
663  char *p_dst, *result = NULL;
664 
665  if (pcmk__str_empty(string) || !match_data) {
666  return NULL;
667  }
668 
669  p = last_match_index = string;
670 
671  while (*p) {
672  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
673  i = *(p + 1) - '0';
674  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
675  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
676  len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
677  last_match_index = p + 2;
678  }
679  p++;
680  }
681  p++;
682  }
683  len += p - last_match_index + 1;
684 
685  /* FIXME: Excessive? */
686  if (len - 1 <= 0) {
687  return NULL;
688  }
689 
690  p_dst = result = calloc(1, len);
691  p = string;
692 
693  while (*p) {
694  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
695  i = *(p + 1) - '0';
696  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
697  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
698  /* rm_eo can be equal to rm_so, but then there is nothing to do */
699  int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
700  memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
701  p_dst += match_len;
702  }
703  p++;
704  } else {
705  *(p_dst) = *(p);
706  p_dst++;
707  }
708  p++;
709  }
710 
711  return result;
712 }
713 
714 #if ENABLE_VERSIONED_ATTRS
715 GHashTable*
716 pe_unpack_versioned_parameters(xmlNode *versioned_params, const char *ra_version)
717 {
718  GHashTable *hash = pcmk__strkey_table(free, free);
719 
720  if (versioned_params && ra_version) {
721  GHashTable *node_hash = pcmk__strkey_table(free, free);
722  xmlNode *attr_set = pcmk__xe_first_child(versioned_params);
723 
724  if (attr_set) {
725  g_hash_table_insert(node_hash, strdup(CRM_ATTR_RA_VERSION),
726  strdup(ra_version));
727  pe_unpack_nvpairs(NULL, versioned_params,
728  crm_element_name(attr_set), node_hash, hash, NULL,
729  FALSE, NULL, NULL);
730  }
731 
732  g_hash_table_destroy(node_hash);
733  }
734 
735  return hash;
736 }
737 #endif
738 
739 gboolean
740 pe_eval_rules(xmlNode *ruleset, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
741 {
742  // If there are no rules, pass by default
743  gboolean ruleset_default = TRUE;
744 
745  for (xmlNode *rule = first_named_child(ruleset, XML_TAG_RULE);
746  rule != NULL; rule = crm_next_same_xml(rule)) {
747 
748  ruleset_default = FALSE;
749  if (pe_eval_expr(rule, rule_data, next_change)) {
750  /* Only the deprecated "lifetime" element of location constraints
751  * may contain more than one rule at the top level -- the schema
752  * limits a block of nvpairs to a single top-level rule. So, this
753  * effectively means that a lifetime is active if any rule it
754  * contains is active.
755  */
756  return TRUE;
757  }
758  }
759 
760  return ruleset_default;
761 }
762 
763 gboolean
764 pe_eval_expr(xmlNode *rule, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
765 {
766  xmlNode *expr = NULL;
767  gboolean test = TRUE;
768  gboolean empty = TRUE;
769  gboolean passed = TRUE;
770  gboolean do_and = TRUE;
771  const char *value = NULL;
772 
773  rule = expand_idref(rule, NULL);
775  if (pcmk__str_eq(value, "or", pcmk__str_casei)) {
776  do_and = FALSE;
777  passed = FALSE;
778  }
779 
780  crm_trace("Testing rule %s", ID(rule));
781  for (expr = pcmk__xe_first_child(rule); expr != NULL;
782  expr = pcmk__xe_next(expr)) {
783 
784  test = pe_eval_subexpr(expr, rule_data, next_change);
785  empty = FALSE;
786 
787  if (test && do_and == FALSE) {
788  crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
789  return TRUE;
790 
791  } else if (test == FALSE && do_and) {
792  crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
793  return FALSE;
794  }
795  }
796 
797  if (empty) {
798  crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
799  }
800 
801  crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
802  return passed;
803 }
804 
805 gboolean
806 pe_eval_subexpr(xmlNode *expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
807 {
808  gboolean accept = FALSE;
809  const char *uname = NULL;
810 
811  switch (find_expression_type(expr)) {
812  case nested_rule:
813  accept = pe_eval_expr(expr, rule_data, next_change);
814  break;
815  case attr_expr:
816  case loc_expr:
817  /* these expressions can never succeed if there is
818  * no node to compare with
819  */
820  if (rule_data->node_hash != NULL) {
821  accept = pe__eval_attr_expr(expr, rule_data);
822  }
823  break;
824 
825  case time_expr:
826  switch (pe__eval_date_expr(expr, rule_data, next_change)) {
828  case pcmk_rc_ok:
829  accept = TRUE;
830  break;
831 
832  default:
833  accept = FALSE;
834  break;
835  }
836  break;
837 
838  case role_expr:
839  accept = pe__eval_role_expr(expr, rule_data);
840  break;
841 
842  case rsc_expr:
843  accept = pe__eval_rsc_expr(expr, rule_data);
844  break;
845 
846  case op_expr:
847  accept = pe__eval_op_expr(expr, rule_data);
848  break;
849 
850 #if ENABLE_VERSIONED_ATTRS
851  case version_expr:
852  if (rule_data->node_hash &&
853  g_hash_table_lookup_extended(rule_data->node_hash,
854  CRM_ATTR_RA_VERSION, NULL, NULL)) {
855  accept = pe__eval_attr_expr(expr, rule_data);
856  } else {
857  // we are going to test it when we have ra-version
858  accept = TRUE;
859  }
860  break;
861 #endif
862 
863  default:
864  CRM_CHECK(FALSE /* bad type */ , return FALSE);
865  accept = FALSE;
866  }
867  if (rule_data->node_hash) {
868  uname = g_hash_table_lookup(rule_data->node_hash, CRM_ATTR_UNAME);
869  }
870 
871  crm_trace("Expression %s %s on %s",
872  ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
873  return accept;
874 }
875 
891 static int
892 compare_attr_expr_vals(const char *l_val, const char *r_val, const char *type,
893  const char *op)
894 {
895  int cmp = 0;
896 
897  if (l_val != NULL && r_val != NULL) {
898  if (type == NULL) {
899  if (pcmk__strcase_any_of(op, "lt", "lte", "gt", "gte", NULL)) {
900  if (pcmk__char_in_any_str('.', l_val, r_val, NULL)) {
901  type = "number";
902  } else {
903  type = "integer";
904  }
905 
906  } else {
907  type = "string";
908  }
909  crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
910  }
911 
912  if (pcmk__str_eq(type, "string", pcmk__str_casei)) {
913  cmp = strcasecmp(l_val, r_val);
914 
915  } else if (pcmk__str_eq(type, "integer", pcmk__str_casei)) {
916  long long l_val_num;
917  int rc1 = pcmk__scan_ll(l_val, &l_val_num, 0LL);
918 
919  long long r_val_num;
920  int rc2 = pcmk__scan_ll(r_val, &r_val_num, 0LL);
921 
922  if ((rc1 == pcmk_rc_ok) && (rc2 == pcmk_rc_ok)) {
923  if (l_val_num < r_val_num) {
924  cmp = -1;
925  } else if (l_val_num > r_val_num) {
926  cmp = 1;
927  } else {
928  cmp = 0;
929  }
930 
931  } else {
932  crm_debug("Integer parse error. Comparing %s and %s as strings",
933  l_val, r_val);
934  cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
935  }
936 
937  } else if (pcmk__str_eq(type, "number", pcmk__str_casei)) {
938  double l_val_num;
939  double r_val_num;
940 
941  int rc1 = pcmk__scan_double(l_val, &l_val_num, NULL, NULL);
942  int rc2 = pcmk__scan_double(r_val, &r_val_num, NULL, NULL);
943 
944  if (rc1 == pcmk_rc_ok && rc2 == pcmk_rc_ok) {
945  if (l_val_num < r_val_num) {
946  cmp = -1;
947  } else if (l_val_num > r_val_num) {
948  cmp = 1;
949  } else {
950  cmp = 0;
951  }
952 
953  } else {
954  crm_debug("Floating-point parse error. Comparing %s and %s as "
955  "strings", l_val, r_val);
956  cmp = compare_attr_expr_vals(l_val, r_val, "string", op);
957  }
958 
959  } else if (pcmk__str_eq(type, "version", pcmk__str_casei)) {
960  cmp = compare_version(l_val, r_val);
961 
962  }
963 
964  } else if (l_val == NULL && r_val == NULL) {
965  cmp = 0;
966  } else if (r_val == NULL) {
967  cmp = 1;
968  } else { // l_val == NULL && r_val != NULL
969  cmp = -1;
970  }
971 
972  return cmp;
973 }
974 
989 static bool
990 accept_attr_expr(const char *l_val, const char *r_val, const char *type,
991  const char *op)
992 {
993  int cmp;
994 
995  if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
996  return (l_val != NULL);
997 
998  } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
999  return (l_val == NULL);
1000 
1001  }
1002 
1003  cmp = compare_attr_expr_vals(l_val, r_val, type, op);
1004 
1005  if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
1006  return (cmp == 0);
1007 
1008  } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
1009  return (cmp != 0);
1010 
1011  } else if (l_val == NULL || r_val == NULL) {
1012  // The comparison is meaningless from this point on
1013  return false;
1014 
1015  } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
1016  return (cmp < 0);
1017 
1018  } else if (pcmk__str_eq(op, "lte", pcmk__str_casei)) {
1019  return (cmp <= 0);
1020 
1021  } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
1022  return (cmp > 0);
1023 
1024  } else if (pcmk__str_eq(op, "gte", pcmk__str_casei)) {
1025  return (cmp >= 0);
1026  }
1027 
1028  return false; // Should never reach this point
1029 }
1030 
1039 static const char *
1040 expand_value_source(const char *value, const char *value_source,
1041  pe_match_data_t *match_data)
1042 {
1043  GHashTable *table = NULL;
1044 
1045  if (pcmk__str_empty(value)) {
1046  return NULL; // value_source is irrelevant
1047 
1048  } else if (pcmk__str_eq(value_source, "param", pcmk__str_casei)) {
1049  table = match_data->params;
1050 
1051  } else if (pcmk__str_eq(value_source, "meta", pcmk__str_casei)) {
1052  table = match_data->meta;
1053 
1054  } else { // literal
1055  return value;
1056  }
1057 
1058  if (table == NULL) {
1059  return NULL;
1060  }
1061  return (const char *) g_hash_table_lookup(table, value);
1062 }
1063 
1074 gboolean
1075 pe__eval_attr_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
1076 {
1077  gboolean attr_allocated = FALSE;
1078  const char *h_val = NULL;
1079 
1080  const char *op = NULL;
1081  const char *type = NULL;
1082  const char *attr = NULL;
1083  const char *value = NULL;
1084  const char *value_source = NULL;
1085 
1088  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
1090  value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
1091 
1092  if (attr == NULL || op == NULL) {
1093  pe_err("Invalid attribute or operation in expression"
1094  " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
1095  return FALSE;
1096  }
1097 
1098  if (rule_data->match_data != NULL) {
1099  // Expand any regular expression submatches (%0-%9) in attribute name
1100  if (rule_data->match_data->re != NULL) {
1101  char *resolved_attr = pe_expand_re_matches(attr, rule_data->match_data->re);
1102 
1103  if (resolved_attr != NULL) {
1104  attr = (const char *) resolved_attr;
1105  attr_allocated = TRUE;
1106  }
1107  }
1108 
1109  // Get value appropriate to value-source
1110  value = expand_value_source(value, value_source, rule_data->match_data);
1111  }
1112 
1113  if (rule_data->node_hash != NULL) {
1114  h_val = (const char *)g_hash_table_lookup(rule_data->node_hash, attr);
1115  }
1116 
1117  if (attr_allocated) {
1118  free((char *)attr);
1119  attr = NULL;
1120  }
1121 
1122  return accept_attr_expr(h_val, value, type, op);
1123 }
1124 
1135 int
1136 pe__eval_date_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
1137 {
1138  crm_time_t *start = NULL;
1139  crm_time_t *end = NULL;
1140  const char *value = NULL;
1141  const char *op = crm_element_value(expr, "operation");
1142 
1143  xmlNode *duration_spec = NULL;
1144  xmlNode *date_spec = NULL;
1145 
1146  // "undetermined" will also be returned for parsing errors
1147  int rc = pcmk_rc_undetermined;
1148 
1149  crm_trace("Testing expression: %s", ID(expr));
1150 
1151  duration_spec = first_named_child(expr, "duration");
1152  date_spec = first_named_child(expr, "date_spec");
1153 
1154  value = crm_element_value(expr, "start");
1155  if (value != NULL) {
1156  start = crm_time_new(value);
1157  }
1158  value = crm_element_value(expr, "end");
1159  if (value != NULL) {
1160  end = crm_time_new(value);
1161  }
1162 
1163  if (start != NULL && end == NULL && duration_spec != NULL) {
1164  end = pe_parse_xml_duration(start, duration_spec);
1165  }
1166 
1167  if (pcmk__str_eq(op, "in_range", pcmk__str_null_matches | pcmk__str_casei)) {
1168  if ((start == NULL) && (end == NULL)) {
1169  // in_range requires at least one of start or end
1170  } else if ((start != NULL) && (crm_time_compare(rule_data->now, start) < 0)) {
1171  rc = pcmk_rc_before_range;
1172  crm_time_set_if_earlier(next_change, start);
1173  } else if ((end != NULL) && (crm_time_compare(rule_data->now, end) > 0)) {
1174  rc = pcmk_rc_after_range;
1175  } else {
1176  rc = pcmk_rc_within_range;
1177  if (end && next_change) {
1178  // Evaluation doesn't change until second after end
1179  crm_time_add_seconds(end, 1);
1180  crm_time_set_if_earlier(next_change, end);
1181  }
1182  }
1183 
1184  } else if (pcmk__str_eq(op, "date_spec", pcmk__str_casei)) {
1185  rc = pe_cron_range_satisfied(rule_data->now, date_spec);
1186  // @TODO set next_change appropriately
1187 
1188  } else if (pcmk__str_eq(op, "gt", pcmk__str_casei)) {
1189  if (start == NULL) {
1190  // gt requires start
1191  } else if (crm_time_compare(rule_data->now, start) > 0) {
1192  rc = pcmk_rc_within_range;
1193  } else {
1194  rc = pcmk_rc_before_range;
1195 
1196  // Evaluation doesn't change until second after start
1197  crm_time_add_seconds(start, 1);
1198  crm_time_set_if_earlier(next_change, start);
1199  }
1200 
1201  } else if (pcmk__str_eq(op, "lt", pcmk__str_casei)) {
1202  if (end == NULL) {
1203  // lt requires end
1204  } else if (crm_time_compare(rule_data->now, end) < 0) {
1205  rc = pcmk_rc_within_range;
1206  crm_time_set_if_earlier(next_change, end);
1207  } else {
1208  rc = pcmk_rc_after_range;
1209  }
1210  }
1211 
1212  crm_time_free(start);
1213  crm_time_free(end);
1214  return rc;
1215 }
1216 
1217 gboolean
1218 pe__eval_op_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data) {
1219  const char *name = crm_element_value(expr, XML_NVPAIR_ATTR_NAME);
1220  const char *interval_s = crm_element_value(expr, XML_LRM_ATTR_INTERVAL);
1221  guint interval;
1222 
1223  crm_trace("Testing op_defaults expression: %s", ID(expr));
1224 
1225  if (rule_data->op_data == NULL) {
1226  crm_trace("No operations data provided");
1227  return FALSE;
1228  }
1229 
1230  interval = crm_parse_interval_spec(interval_s);
1231  if (interval == 0 && errno != 0) {
1232  crm_trace("Could not parse interval: %s", interval_s);
1233  return FALSE;
1234  }
1235 
1236  if (interval_s != NULL && interval != rule_data->op_data->interval) {
1237  crm_trace("Interval doesn't match: %d != %d", interval, rule_data->op_data->interval);
1238  return FALSE;
1239  }
1240 
1241  if (!pcmk__str_eq(name, rule_data->op_data->op_name, pcmk__str_none)) {
1242  crm_trace("Name doesn't match: %s != %s", name, rule_data->op_data->op_name);
1243  return FALSE;
1244  }
1245 
1246  return TRUE;
1247 }
1248 
1258 gboolean
1259 pe__eval_role_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
1260 {
1261  gboolean accept = FALSE;
1262  const char *op = NULL;
1263  const char *value = NULL;
1264 
1265  if (rule_data->role == RSC_ROLE_UNKNOWN) {
1266  return accept;
1267  }
1268 
1269  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
1271 
1272  if (pcmk__str_eq(op, "defined", pcmk__str_casei)) {
1273  if (rule_data->role > RSC_ROLE_STARTED) {
1274  accept = TRUE;
1275  }
1276 
1277  } else if (pcmk__str_eq(op, "not_defined", pcmk__str_casei)) {
1278  if ((rule_data->role > RSC_ROLE_UNKNOWN)
1279  && (rule_data->role < RSC_ROLE_UNPROMOTED)) {
1280  accept = TRUE;
1281  }
1282 
1283  } else if (pcmk__str_eq(op, "eq", pcmk__str_casei)) {
1284  if (text2role(value) == rule_data->role) {
1285  accept = TRUE;
1286  }
1287 
1288  } else if (pcmk__str_eq(op, "ne", pcmk__str_casei)) {
1289  // Test "ne" only with promotable clone roles
1290  if ((rule_data->role > RSC_ROLE_UNKNOWN)
1291  && (rule_data->role < RSC_ROLE_UNPROMOTED)) {
1292  accept = FALSE;
1293 
1294  } else if (text2role(value) != rule_data->role) {
1295  accept = TRUE;
1296  }
1297  }
1298  return accept;
1299 }
1300 
1301 gboolean
1302 pe__eval_rsc_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
1303 {
1304  const char *class = crm_element_value(expr, XML_AGENT_ATTR_CLASS);
1305  const char *provider = crm_element_value(expr, XML_AGENT_ATTR_PROVIDER);
1306  const char *type = crm_element_value(expr, XML_EXPR_ATTR_TYPE);
1307 
1308  crm_trace("Testing rsc_defaults expression: %s", ID(expr));
1309 
1310  if (rule_data->rsc_data == NULL) {
1311  crm_trace("No resource data provided");
1312  return FALSE;
1313  }
1314 
1315  if (class != NULL &&
1316  !pcmk__str_eq(class, rule_data->rsc_data->standard, pcmk__str_none)) {
1317  crm_trace("Class doesn't match: %s != %s", class, rule_data->rsc_data->standard);
1318  return FALSE;
1319  }
1320 
1321  if ((provider == NULL && rule_data->rsc_data->provider != NULL) ||
1322  (provider != NULL && rule_data->rsc_data->provider == NULL) ||
1323  !pcmk__str_eq(provider, rule_data->rsc_data->provider, pcmk__str_none)) {
1324  crm_trace("Provider doesn't match: %s != %s", provider, rule_data->rsc_data->provider);
1325  return FALSE;
1326  }
1327 
1328  if (type != NULL &&
1329  !pcmk__str_eq(type, rule_data->rsc_data->agent, pcmk__str_none)) {
1330  crm_trace("Agent doesn't match: %s != %s", type, rule_data->rsc_data->agent);
1331  return FALSE;
1332  }
1333 
1334  return TRUE;
1335 }
1336 
1337 // Deprecated functions kept only for backward API compatibility
1338 // LCOV_EXCL_START
1339 
1340 #include <crm/pengine/rules_compat.h>
1341 
1342 gboolean
1343 test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
1344 {
1345  return pe_evaluate_rules(ruleset, node_hash, now, NULL);
1346 }
1347 
1348 gboolean
1349 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1350 {
1351  return pe_test_rule(rule, node_hash, role, now, NULL, NULL);
1352 }
1353 
1354 gboolean
1355 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)
1356 {
1357  pe_match_data_t match_data = {
1358  .re = re_match_data,
1359  .params = NULL,
1360  .meta = NULL,
1361  };
1362  return pe_test_rule(rule, node_hash, role, now, NULL, &match_data);
1363 }
1364 
1365 gboolean
1366 pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
1367  crm_time_t *now, pe_match_data_t *match_data)
1368 {
1369  return pe_test_rule(rule, node_hash, role, now, NULL, match_data);
1370 }
1371 
1372 gboolean
1373 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1374 {
1375  return pe_test_expression(expr, node_hash, role, now, NULL, NULL);
1376 }
1377 
1378 gboolean
1379 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)
1380 {
1381  pe_match_data_t match_data = {
1382  .re = re_match_data,
1383  .params = NULL,
1384  .meta = NULL,
1385  };
1386  return pe_test_expression(expr, node_hash, role, now, NULL, &match_data);
1387 }
1388 
1389 gboolean
1390 pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
1391  enum rsc_role_e role, crm_time_t *now,
1392  pe_match_data_t *match_data)
1393 {
1394  return pe_test_expression(expr, node_hash, role, now, NULL, match_data);
1395 }
1396 
1397 void
1398 unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
1399  GHashTable *node_hash, GHashTable *hash,
1400  const char *always_first, gboolean overwrite,
1401  crm_time_t *now)
1402 {
1403  pe_rule_eval_data_t rule_data = {
1404  .node_hash = node_hash,
1405  .role = RSC_ROLE_UNKNOWN,
1406  .now = now,
1407  .match_data = NULL,
1408  .rsc_data = NULL,
1409  .op_data = NULL
1410  };
1411 
1412  unpack_nvpair_blocks(top, xml_obj, set_name, hash, always_first,
1413  overwrite, &rule_data, NULL, unpack_attr_set);
1414 }
1415 
1416 // LCOV_EXCL_STOP
1417 // End deprecated API
crm_time_t * crm_time_new_undefined(void)
Allocate memory for an uninitialized time object.
Definition: iso8601.c:116
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
A dumping ground.
void crm_time_add_years(crm_time_t *dt, int value)
Definition: iso8601.c:1522
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:1355
const char * provider
Definition: common.h:184
Definition: rules.h:30
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:1417
char data[0]
Definition: cpg.c:55
bool pcmk__char_in_any_str(int ch,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:978
#define CRM_ATTR_KIND
Definition: crm.h:116
#define XML_EXPR_ATTR_TYPE
Definition: msg_xml.h:348
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition: iso8601.c:132
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:931
struct crm_time_s crm_time_t
Definition: iso8601.h:32
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1373
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:339
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2794
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
pe_re_match_data_t * re
Definition: common.h:177
#define XML_TAG_ATTRS
Definition: msg_xml.h:211
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:1379
#define XML_LRM_ATTR_INTERVAL
Definition: msg_xml.h:297
#define CRM_ATTR_ROLE
Definition: crm.h:117
#define XML_EXPR_ATTR_VALUE_SOURCE
Definition: msg_xml.h:349
void pe_unpack_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition: rules.c:628
int crm_time_get_ordinal(crm_time_t *dt, uint32_t *y, uint32_t *d)
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:383
#define CHECK_ONE(spec, name, var)
Definition: rules.c:235
enum crm_ais_msg_types type
Definition: cpg.c:48
#define CRM_ATTR_RA_VERSION
Definition: crm.h:124
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:206
Definition: rules.h:31
gboolean pe_eval_rules(xmlNode *ruleset, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:740
void crm_time_add_hours(crm_time_t *dt, int value)
Definition: iso8601.c:1510
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:1390
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:830
guint interval
Definition: common.h:190
pe_match_data_t * match_data
Definition: common.h:197
int pe__eval_date_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:1136
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:199
Definition: rules.h:23
#define crm_debug(fmt, args...)
Definition: logging.h:363
gboolean pe_eval_subexpr(xmlNode *expr, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:806
void crm_time_add_weeks(crm_time_t *dt, int value)
Definition: iso8601.c:1516
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:529
#define XML_EXPR_ATTR_VALUE
Definition: msg_xml.h:347
void crm_time_add_months(crm_time_t *dt, int value)
Definition: iso8601.c:1462
#define CRM_ATTR_UNAME
Definition: crm.h:114
#define crm_trace(fmt, args...)
Definition: logging.h:364
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2867
const char * op_name
Definition: common.h:189
#define XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:273
Wrappers for and extensions to libxml2.
char * pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
Definition: rules.c:658
void crm_time_add_minutes(crm_time_t *dt, int value)
Definition: iso8601.c:1504
#define XML_EXPR_ATTR_OPERATION
Definition: msg_xml.h:346
gboolean pe__eval_role_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:1259
GHashTable * meta
Definition: common.h:179
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
void free_xml(xmlNode *child)
Definition: xml.c:824
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:1366
enum rsc_role_e text2role(const char *role)
Definition: common.c:483
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:198
const char * agent
Definition: common.h:185
gboolean pe_eval_expr(xmlNode *rule, pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:764
#define XML_RULE_ATTR_BOOLEAN_OP
Definition: msg_xml.h:342
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1349
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
crm_time_t * pe_parse_xml_duration(crm_time_t *start, xmlNode *duration_spec)
Definition: rules.c:289
struct unpack_data_s unpack_data_t
CRM_TRACE_INIT_DATA(pe_rules)
GHashTable * params
Definition: common.h:178
struct sorted_set_s sorted_set_t
void pe_eval_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name, pe_rule_eval_data_t *rule_data, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *next_change)
Definition: rules.c:605
const char * standard
Definition: common.h:183
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:611
pcmk__action_result_t result
Definition: pcmk_fence.c:34
crm_time_t * now
Definition: common.h:196
int crm_time_get_gregorian(crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
#define crm_err(fmt, args...)
Definition: logging.h:358
int crm_time_get_timeofday(crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
GHashTable * node_hash
Definition: common.h:194
char guint crm_parse_interval_spec(const char *input)
Parse milliseconds from a Pacemaker interval specification.
Definition: utils.c:242
gboolean pe__eval_op_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:1218
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition: rules.c:1343
void crm_time_set(crm_time_t *target, crm_time_t *source)
Definition: iso8601.c:1192
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:92
regmatch_t * pmatch
Definition: common.h:173
int compare_version(const char *version1, const char *version2)
Definition: utils.c:160
gboolean pe__eval_rsc_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:1302
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:384
char uname[MAX_NAME]
Definition: cpg.c:50
gboolean pe__eval_attr_expr(xmlNodePtr expr, pe_rule_eval_data_t *rule_data)
Definition: rules.c:1075
#define crm_str(x)
Definition: logging.h:384
int crm_time_compare(crm_time_t *dt, crm_time_t *rhs)
Definition: iso8601.c:1387
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:688
int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:813
Definition: rules.h:26
expression_type
Definition: rules.h:22
#define ID(x)
Definition: msg_xml.h:460
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:1398
#define pe_err(fmt...)
Definition: internal.h:24
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:105
#define XML_TAG_RULE
Definition: msg_xml.h:338
enum rsc_role_e role
Definition: common.h:195
#define CRM_ATTR_ID
Definition: crm.h:115
int pe_cron_range_satisfied(crm_time_t *now, xmlNode *cron_spec)
Definition: rules.c:243
int crm_time_get_isoweek(crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
#define XML_EXPR_ATTR_ATTRIBUTE
Definition: msg_xml.h:345
void crm_time_add_days(crm_time_t *dt, int value)
Definition: iso8601.c:1437
char * string
Definition: common.h:171
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:272
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:2820
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:140