pacemaker  2.0.4-2deceaa
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rules.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2019 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>
14 
15 #include <glib.h>
16 
17 #include <crm/pengine/rules.h>
19 #include <crm/pengine/internal.h>
20 
21 #include <sys/types.h>
22 #include <regex.h>
23 #include <ctype.h>
24 
25 CRM_TRACE_INIT_DATA(pe_rules);
26 
37 gboolean
38 pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now,
39  crm_time_t *next_change)
40 {
41  // If there are no rules, pass by default
42  gboolean ruleset_default = TRUE;
43 
44  for (xmlNode *rule = first_named_child(ruleset, XML_TAG_RULE);
45  rule != NULL; rule = crm_next_same_xml(rule)) {
46 
47  ruleset_default = FALSE;
48  if (pe_test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now, next_change,
49  NULL)) {
50  /* Only the deprecated "lifetime" element of location constraints
51  * may contain more than one rule at the top level -- the schema
52  * limits a block of nvpairs to a single top-level rule. So, this
53  * effectively means that a lifetime is active if any rule it
54  * contains is active.
55  */
56  return TRUE;
57  }
58  }
59  return ruleset_default;
60 }
61 
62 gboolean
63 pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
64  crm_time_t *now, crm_time_t *next_change,
65  pe_match_data_t *match_data)
66 {
67  xmlNode *expr = NULL;
68  gboolean test = TRUE;
69  gboolean empty = TRUE;
70  gboolean passed = TRUE;
71  gboolean do_and = TRUE;
72  const char *value = NULL;
73 
74  rule = expand_idref(rule, NULL);
76  if (safe_str_eq(value, "or")) {
77  do_and = FALSE;
78  passed = FALSE;
79  }
80 
81  crm_trace("Testing rule %s", ID(rule));
82  for (expr = __xml_first_child_element(rule); expr != NULL;
83  expr = __xml_next_element(expr)) {
84 
85  test = pe_test_expression(expr, node_hash, role, now, next_change,
86  match_data);
87  empty = FALSE;
88 
89  if (test && do_and == FALSE) {
90  crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
91  return TRUE;
92 
93  } else if (test == FALSE && do_and) {
94  crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
95  return FALSE;
96  }
97  }
98 
99  if (empty) {
100  crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
101  }
102 
103  crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
104  return passed;
105 }
106 
123 gboolean
124 pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role,
125  crm_time_t *now, crm_time_t *next_change,
126  pe_match_data_t *match_data)
127 {
128  gboolean accept = FALSE;
129  const char *uname = NULL;
130 
131  switch (find_expression_type(expr)) {
132  case nested_rule:
133  accept = pe_test_rule(expr, node_hash, role, now, next_change,
134  match_data);
135  break;
136  case attr_expr:
137  case loc_expr:
138  /* these expressions can never succeed if there is
139  * no node to compare with
140  */
141  if (node_hash != NULL) {
142  accept = pe_test_attr_expression(expr, node_hash, now, match_data);
143  }
144  break;
145 
146  case time_expr:
147  accept = pe_test_date_expression(expr, now, next_change);
148  break;
149 
150  case role_expr:
151  accept = pe_test_role_expression(expr, role, now);
152  break;
153 
154 #if ENABLE_VERSIONED_ATTRS
155  case version_expr:
156  if (node_hash && g_hash_table_lookup_extended(node_hash,
158  NULL, NULL)) {
159  accept = pe_test_attr_expression(expr, node_hash, now, NULL);
160  } else {
161  // we are going to test it when we have ra-version
162  accept = TRUE;
163  }
164  break;
165 #endif
166 
167  default:
168  CRM_CHECK(FALSE /* bad type */ , return FALSE);
169  accept = FALSE;
170  }
171  if (node_hash) {
172  uname = g_hash_table_lookup(node_hash, CRM_ATTR_UNAME);
173  }
174 
175  crm_trace("Expression %s %s on %s",
176  ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
177  return accept;
178 }
179 
180 enum expression_type
181 find_expression_type(xmlNode * expr)
182 {
183  const char *tag = NULL;
184  const char *attr = NULL;
185 
187  tag = crm_element_name(expr);
188 
189  if (safe_str_eq(tag, "date_expression")) {
190  return time_expr;
191 
192  } else if (safe_str_eq(tag, XML_TAG_RULE)) {
193  return nested_rule;
194 
195  } else if (safe_str_neq(tag, "expression")) {
196  return not_expr;
197 
198  } else if (safe_str_eq(attr, CRM_ATTR_UNAME)
199  || safe_str_eq(attr, CRM_ATTR_KIND)
200  || safe_str_eq(attr, CRM_ATTR_ID)) {
201  return loc_expr;
202 
203  } else if (safe_str_eq(attr, CRM_ATTR_ROLE)) {
204  return role_expr;
205 
206 #if ENABLE_VERSIONED_ATTRS
207  } else if (safe_str_eq(attr, CRM_ATTR_RA_VERSION)) {
208  return version_expr;
209 #endif
210  }
211 
212  return attr_expr;
213 }
214 
215 gboolean
216 pe_test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now)
217 {
218  gboolean accept = FALSE;
219  const char *op = NULL;
220  const char *value = NULL;
221 
222  if (role == RSC_ROLE_UNKNOWN) {
223  return accept;
224  }
225 
226  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
228 
229  if (safe_str_eq(op, "defined")) {
230  if (role > RSC_ROLE_STARTED) {
231  accept = TRUE;
232  }
233 
234  } else if (safe_str_eq(op, "not_defined")) {
235  if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
236  accept = TRUE;
237  }
238 
239  } else if (safe_str_eq(op, "eq")) {
240  if (text2role(value) == role) {
241  accept = TRUE;
242  }
243 
244  } else if (safe_str_eq(op, "ne")) {
245  // Test "ne" only with promotable clone roles
246  if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
247  accept = FALSE;
248 
249  } else if (text2role(value) != role) {
250  accept = TRUE;
251  }
252  }
253  return accept;
254 }
255 
256 gboolean
257 pe_test_attr_expression(xmlNode *expr, GHashTable *hash, crm_time_t *now,
258  pe_match_data_t *match_data)
259 {
260  gboolean accept = FALSE;
261  gboolean attr_allocated = FALSE;
262  int cmp = 0;
263  const char *h_val = NULL;
264  GHashTable *table = NULL;
265 
266  const char *op = NULL;
267  const char *type = NULL;
268  const char *attr = NULL;
269  const char *value = NULL;
270  const char *value_source = NULL;
271 
274  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
276  value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
277 
278  if (attr == NULL || op == NULL) {
279  pe_err("Invalid attribute or operation in expression"
280  " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
281  return FALSE;
282  }
283 
284  if (match_data) {
285  if (match_data->re) {
286  char *resolved_attr = pe_expand_re_matches(attr, match_data->re);
287 
288  if (resolved_attr) {
289  attr = (const char *) resolved_attr;
290  attr_allocated = TRUE;
291  }
292  }
293 
294  if (safe_str_eq(value_source, "param")) {
295  table = match_data->params;
296  } else if (safe_str_eq(value_source, "meta")) {
297  table = match_data->meta;
298  }
299  }
300 
301  if (table) {
302  const char *param_name = value;
303  const char *param_value = NULL;
304 
305  if (param_name && param_name[0]) {
306  if ((param_value = (const char *)g_hash_table_lookup(table, param_name))) {
307  value = param_value;
308  }
309  }
310  }
311 
312  if (hash != NULL) {
313  h_val = (const char *)g_hash_table_lookup(hash, attr);
314  }
315 
316  if (attr_allocated) {
317  free((char *)attr);
318  attr = NULL;
319  }
320 
321  if (value != NULL && h_val != NULL) {
322  if (type == NULL) {
323  if (safe_str_eq(op, "lt")
324  || safe_str_eq(op, "lte")
325  || safe_str_eq(op, "gt")
326  || safe_str_eq(op, "gte")) {
327  type = "number";
328 
329  } else {
330  type = "string";
331  }
332  crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
333  }
334 
335  if (safe_str_eq(type, "string")) {
336  cmp = strcasecmp(h_val, value);
337 
338  } else if (safe_str_eq(type, "number")) {
339  int h_val_f = crm_parse_int(h_val, NULL);
340  int value_f = crm_parse_int(value, NULL);
341 
342  if (h_val_f < value_f) {
343  cmp = -1;
344  } else if (h_val_f > value_f) {
345  cmp = 1;
346  } else {
347  cmp = 0;
348  }
349 
350  } else if (safe_str_eq(type, "version")) {
351  cmp = compare_version(h_val, value);
352 
353  }
354 
355  } else if (value == NULL && h_val == NULL) {
356  cmp = 0;
357  } else if (value == NULL) {
358  cmp = 1;
359  } else {
360  cmp = -1;
361  }
362 
363  if (safe_str_eq(op, "defined")) {
364  if (h_val != NULL) {
365  accept = TRUE;
366  }
367 
368  } else if (safe_str_eq(op, "not_defined")) {
369  if (h_val == NULL) {
370  accept = TRUE;
371  }
372 
373  } else if (safe_str_eq(op, "eq")) {
374  if ((h_val == value) || cmp == 0) {
375  accept = TRUE;
376  }
377 
378  } else if (safe_str_eq(op, "ne")) {
379  if ((h_val == NULL && value != NULL)
380  || (h_val != NULL && value == NULL)
381  || cmp != 0) {
382  accept = TRUE;
383  }
384 
385  } else if (value == NULL || h_val == NULL) {
386  // The comparison is meaningless from this point on
387  accept = FALSE;
388 
389  } else if (safe_str_eq(op, "lt")) {
390  if (cmp < 0) {
391  accept = TRUE;
392  }
393 
394  } else if (safe_str_eq(op, "lte")) {
395  if (cmp <= 0) {
396  accept = TRUE;
397  }
398 
399  } else if (safe_str_eq(op, "gt")) {
400  if (cmp > 0) {
401  accept = TRUE;
402  }
403 
404  } else if (safe_str_eq(op, "gte")) {
405  if (cmp >= 0) {
406  accept = TRUE;
407  }
408  }
409 
410  return accept;
411 }
412 
413 /* As per the nethack rules:
414  *
415  * moon period = 29.53058 days ~= 30, year = 365.2422 days
416  * days moon phase advances on first day of year compared to preceding year
417  * = 365.2422 - 12*29.53058 ~= 11
418  * years in Metonic cycle (time until same phases fall on the same days of
419  * the month) = 18.6 ~= 19
420  * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
421  * (29 as initial condition)
422  * current phase in days = first day phase + days elapsed in year
423  * 6 moons ~= 177 days
424  * 177 ~= 8 reported phases * 22
425  * + 11/22 for rounding
426  *
427  * 0-7, with 0: new, 4: full
428  */
429 
430 static int
431 phase_of_the_moon(crm_time_t * now)
432 {
433  uint32_t epact, diy, goldn;
434  uint32_t y;
435 
436  crm_time_get_ordinal(now, &y, &diy);
437 
438  goldn = (y % 19) + 1;
439  epact = (11 * goldn + 18) % 30;
440  if ((epact == 25 && goldn > 11) || epact == 24)
441  epact++;
442 
443  return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
444 }
445 
446 static int
447 check_one(xmlNode *cron_spec, const char *xml_field, uint32_t time_field) {
448  int rc = pcmk_rc_undetermined;
449  const char *value = crm_element_value(cron_spec, xml_field);
450  long long low, high;
451 
452  if (value == NULL) {
453  /* Return pe_date_result_undetermined if the field is missing. */
454  goto bail;
455  }
456 
457  if (pcmk__parse_ll_range(value, &low, &high) == pcmk_rc_unknown_format) {
458  goto bail;
459  } else if (low == high) {
460  /* A single number was given, not a range. */
461  if (time_field < low) {
463  } else if (time_field > high) {
464  rc = pcmk_rc_after_range;
465  } else {
467  }
468  } else if (low != -1 && high != -1) {
469  /* This is a range with both bounds. */
470  if (time_field < low) {
472  } else if (time_field > high) {
473  rc = pcmk_rc_after_range;
474  } else {
476  }
477  } else if (low == -1) {
478  /* This is a range with no starting value. */
479  rc = time_field <= high ? pcmk_rc_within_range : pcmk_rc_after_range;
480  } else if (high == -1) {
481  /* This is a range with no ending value. */
482  rc = time_field >= low ? pcmk_rc_within_range : pcmk_rc_before_range;
483  }
484 
485 bail:
486  if (rc == pcmk_rc_within_range) {
487  crm_debug("Condition '%s' in %s: passed", value, xml_field);
488  } else {
489  crm_debug("Condition '%s' in %s: failed", value, xml_field);
490  }
491 
492  return rc;
493 }
494 
495 static gboolean
496 check_passes(int rc) {
497  /* _within_range is obvious. _undetermined is a pass because
498  * this is the return value if a field is not given. In this
499  * case, we just want to ignore it and check other fields to
500  * see if they place some restriction on what can pass.
501  */
502  return rc == pcmk_rc_within_range || rc == pcmk_rc_undetermined;
503 }
504 
505 #define CHECK_ONE(spec, name, var) do { \
506  int subpart_rc = check_one(spec, name, var); \
507  if (check_passes(subpart_rc) == FALSE) { \
508  return subpart_rc; \
509  } \
510 } while (0)
511 
512 int
513 pe_cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec)
514 {
515  uint32_t h, m, s, y, d, w;
516 
517  CRM_CHECK(now != NULL, return pcmk_rc_op_unsatisfied);
518 
519  crm_time_get_gregorian(now, &y, &m, &d);
520  CHECK_ONE(cron_spec, "years", y);
521  CHECK_ONE(cron_spec, "months", m);
522  CHECK_ONE(cron_spec, "monthdays", d);
523 
524  crm_time_get_timeofday(now, &h, &m, &s);
525  CHECK_ONE(cron_spec, "hours", h);
526  CHECK_ONE(cron_spec, "minutes", m);
527  CHECK_ONE(cron_spec, "seconds", s);
528 
529  crm_time_get_ordinal(now, &y, &d);
530  CHECK_ONE(cron_spec, "yeardays", d);
531 
532  crm_time_get_isoweek(now, &y, &w, &d);
533  CHECK_ONE(cron_spec, "weekyears", y);
534  CHECK_ONE(cron_spec, "weeks", w);
535  CHECK_ONE(cron_spec, "weekdays", d);
536 
537  CHECK_ONE(cron_spec, "moon", phase_of_the_moon(now));
538 
539  /* If we get here, either no fields were specified (which is success), or all
540  * the fields that were specified had their conditions met (which is also a
541  * success). Thus, the result is success.
542  */
543  return pcmk_rc_ok;
544 }
545 
546 #define update_field(xml_field, time_fn) \
547  value = crm_element_value(duration_spec, xml_field); \
548  if(value != NULL) { \
549  int value_i = crm_parse_int(value, "0"); \
550  time_fn(end, value_i); \
551  }
552 
553 crm_time_t *
554 pe_parse_xml_duration(crm_time_t * start, xmlNode * duration_spec)
555 {
556  crm_time_t *end = NULL;
557  const char *value = NULL;
558 
559  end = crm_time_new(NULL);
560  crm_time_set(end, start);
561 
569 
570  return end;
571 }
572 
583 gboolean
585  crm_time_t *next_change)
586 {
587  switch (pe_eval_date_expression(time_expr, now, next_change)) {
589  case pcmk_rc_ok:
590  return TRUE;
591 
592  default:
593  return FALSE;
594  }
595 }
596 
597 // Set next_change to t if t is earlier
598 static void
599 crm_time_set_if_earlier(crm_time_t *next_change, crm_time_t *t)
600 {
601  if ((next_change != NULL) && (t != NULL)) {
602  if (!crm_time_is_defined(next_change)
603  || (crm_time_compare(t, next_change) < 0)) {
604  crm_time_set(next_change, t);
605  }
606  }
607 }
608 
619 int
621  crm_time_t *next_change)
622 {
623  crm_time_t *start = NULL;
624  crm_time_t *end = NULL;
625  const char *value = NULL;
626  const char *op = crm_element_value(time_expr, "operation");
627 
628  xmlNode *duration_spec = NULL;
629  xmlNode *date_spec = NULL;
630 
631  // "undetermined" will also be returned for parsing errors
632  int rc = pcmk_rc_undetermined;
633 
634  crm_trace("Testing expression: %s", ID(time_expr));
635 
636  duration_spec = first_named_child(time_expr, "duration");
637  date_spec = first_named_child(time_expr, "date_spec");
638 
639  value = crm_element_value(time_expr, "start");
640  if (value != NULL) {
641  start = crm_time_new(value);
642  }
643  value = crm_element_value(time_expr, "end");
644  if (value != NULL) {
645  end = crm_time_new(value);
646  }
647 
648  if (start != NULL && end == NULL && duration_spec != NULL) {
649  end = pe_parse_xml_duration(start, duration_spec);
650  }
651 
652  if ((op == NULL) || safe_str_eq(op, "in_range")) {
653  if ((start == NULL) && (end == NULL)) {
654  // in_range requires at least one of start or end
655  } else if ((start != NULL) && (crm_time_compare(now, start) < 0)) {
657  crm_time_set_if_earlier(next_change, start);
658  } else if ((end != NULL) && (crm_time_compare(now, end) > 0)) {
659  rc = pcmk_rc_after_range;
660  } else {
662  if (end && next_change) {
663  // Evaluation doesn't change until second after end
664  crm_time_add_seconds(end, 1);
665  crm_time_set_if_earlier(next_change, end);
666  }
667  }
668 
669  } else if (safe_str_eq(op, "date_spec")) {
670  rc = pe_cron_range_satisfied(now, date_spec);
671  // @TODO set next_change appropriately
672 
673  } else if (safe_str_eq(op, "gt")) {
674  if (start == NULL) {
675  // gt requires start
676  } else if (crm_time_compare(now, start) > 0) {
678  } else {
680 
681  // Evaluation doesn't change until second after start
682  crm_time_add_seconds(start, 1);
683  crm_time_set_if_earlier(next_change, start);
684  }
685 
686  } else if (safe_str_eq(op, "lt")) {
687  if (end == NULL) {
688  // lt requires end
689  } else if (crm_time_compare(now, end) < 0) {
691  crm_time_set_if_earlier(next_change, end);
692  } else {
693  rc = pcmk_rc_after_range;
694  }
695  }
696 
697  crm_time_free(start);
698  crm_time_free(end);
699  return rc;
700 }
701 
702 // Information about a block of nvpair elements
703 typedef struct sorted_set_s {
704  int score; // This block's score for sorting
705  const char *name; // This block's ID
706  const char *special_name; // ID that should sort first
707  xmlNode *attr_set; // This block
708 } sorted_set_t;
709 
710 static gint
711 sort_pairs(gconstpointer a, gconstpointer b)
712 {
713  const sorted_set_t *pair_a = a;
714  const sorted_set_t *pair_b = b;
715 
716  if (a == NULL && b == NULL) {
717  return 0;
718  } else if (a == NULL) {
719  return 1;
720  } else if (b == NULL) {
721  return -1;
722  }
723 
724  if (safe_str_eq(pair_a->name, pair_a->special_name)) {
725  return -1;
726 
727  } else if (safe_str_eq(pair_b->name, pair_a->special_name)) {
728  return 1;
729  }
730 
731  if (pair_a->score < pair_b->score) {
732  return 1;
733  } else if (pair_a->score > pair_b->score) {
734  return -1;
735  }
736  return 0;
737 }
738 
739 static void
740 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
741 {
742  const char *name = NULL;
743  const char *value = NULL;
744  const char *old_value = NULL;
745  xmlNode *list = nvpair_list;
746  xmlNode *an_attr = NULL;
747 
748  name = crm_element_name(list->children);
749  if (safe_str_eq(XML_TAG_ATTRS, name)) {
750  list = list->children;
751  }
752 
753  for (an_attr = __xml_first_child_element(list); an_attr != NULL;
754  an_attr = __xml_next_element(an_attr)) {
755 
756  if (crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) {
757  xmlNode *ref_nvpair = expand_idref(an_attr, top);
758 
759  name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME);
760  if (name == NULL) {
761  name = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_NAME);
762  }
763 
764  crm_trace("Setting attribute: %s", name);
765  value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
766  if (value == NULL) {
767  value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
768  }
769 
770  if (name == NULL || value == NULL) {
771  continue;
772 
773  }
774 
775  old_value = g_hash_table_lookup(hash, name);
776 
777  if (safe_str_eq(value, "#default")) {
778  if (old_value) {
779  crm_trace("Removing value for %s (%s)", name, value);
780  g_hash_table_remove(hash, name);
781  }
782  continue;
783 
784  } else if (old_value == NULL) {
785  g_hash_table_insert(hash, strdup(name), strdup(value));
786 
787  } else if (overwrite) {
788  crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value);
789  g_hash_table_replace(hash, strdup(name), strdup(value));
790  }
791  }
792  }
793 }
794 
795 #if ENABLE_VERSIONED_ATTRS
796 static xmlNode*
797 get_versioned_rule(xmlNode * attr_set)
798 {
799  xmlNode * rule = NULL;
800  xmlNode * expr = NULL;
801 
802  for (rule = __xml_first_child_element(attr_set); rule != NULL;
803  rule = __xml_next_element(rule)) {
804 
805  if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
806  for (expr = __xml_first_child_element(rule); expr != NULL;
807  expr = __xml_next_element(expr)) {
808 
809  if (find_expression_type(expr) == version_expr) {
810  return rule;
811  }
812  }
813  }
814  }
815 
816  return NULL;
817 }
818 
819 static void
820 add_versioned_attributes(xmlNode * attr_set, xmlNode * versioned_attrs)
821 {
822  xmlNode *attr_set_copy = NULL;
823  xmlNode *rule = NULL;
824  xmlNode *expr = NULL;
825 
826  if (!attr_set || !versioned_attrs) {
827  return;
828  }
829 
830  attr_set_copy = copy_xml(attr_set);
831 
832  rule = get_versioned_rule(attr_set_copy);
833  if (!rule) {
834  free_xml(attr_set_copy);
835  return;
836  }
837 
838  expr = __xml_first_child_element(rule);
839  while (expr != NULL) {
840  if (find_expression_type(expr) != version_expr) {
841  xmlNode *node = expr;
842 
843  expr = __xml_next_element(expr);
844  free_xml(node);
845  } else {
846  expr = __xml_next_element(expr);
847  }
848  }
849 
850  add_node_nocopy(versioned_attrs, NULL, attr_set_copy);
851 }
852 #endif
853 
854 typedef struct unpack_data_s {
855  gboolean overwrite;
856  GHashTable *node_hash;
857  void *hash;
858  crm_time_t *now;
859  crm_time_t *next_change;
860  xmlNode *top;
861 } unpack_data_t;
862 
863 static void
864 unpack_attr_set(gpointer data, gpointer user_data)
865 {
866  sorted_set_t *pair = data;
867  unpack_data_t *unpack_data = user_data;
868 
869  if (!pe_evaluate_rules(pair->attr_set, unpack_data->node_hash,
870  unpack_data->now, unpack_data->next_change)) {
871  return;
872  }
873 
874 #if ENABLE_VERSIONED_ATTRS
875  if (get_versioned_rule(pair->attr_set) && !(unpack_data->node_hash &&
876  g_hash_table_lookup_extended(unpack_data->node_hash,
877  CRM_ATTR_RA_VERSION, NULL, NULL))) {
878  // we haven't actually tested versioned expressions yet
879  return;
880  }
881 #endif
882 
883  crm_trace("Adding attributes from %s", pair->name);
884  populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
885 }
886 
887 #if ENABLE_VERSIONED_ATTRS
888 static void
889 unpack_versioned_attr_set(gpointer data, gpointer user_data)
890 {
891  sorted_set_t *pair = data;
892  unpack_data_t *unpack_data = user_data;
893 
894  if (pe_evaluate_rules(pair->attr_set, unpack_data->node_hash,
895  unpack_data->now, unpack_data->next_change)) {
896  add_versioned_attributes(pair->attr_set, unpack_data->hash);
897  }
898 }
899 #endif
900 
912 static GList *
913 make_pairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
914  const char *always_first)
915 {
916  GListPtr unsorted = NULL;
917  const char *score = NULL;
918  sorted_set_t *pair = NULL;
919  xmlNode *attr_set = NULL;
920 
921  if (xml_obj == NULL) {
922  crm_trace("No instance attributes");
923  return NULL;
924  }
925 
926  crm_trace("Checking for attributes");
927  for (attr_set = __xml_first_child_element(xml_obj); attr_set != NULL;
928  attr_set = __xml_next_element(attr_set)) {
929 
930  /* Uncertain if set_name == NULL check is strictly necessary here */
931  if (set_name == NULL || crm_str_eq((const char *)attr_set->name, set_name, TRUE)) {
932  pair = NULL;
933  attr_set = expand_idref(attr_set, top);
934  if (attr_set == NULL) {
935  continue;
936  }
937 
938  pair = calloc(1, sizeof(sorted_set_t));
939  pair->name = ID(attr_set);
940  pair->special_name = always_first;
941  pair->attr_set = attr_set;
942 
943  score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE);
944  pair->score = char2score(score);
945 
946  unsorted = g_list_prepend(unsorted, pair);
947  }
948  }
949  return g_list_sort(unsorted, sort_pairs);
950 }
951 
967 static void
968 unpack_nvpair_blocks(xmlNode *top, xmlNode *xml_obj, const char *set_name,
969  GHashTable *node_hash, void *hash,
970  const char *always_first, gboolean overwrite,
971  crm_time_t *now, crm_time_t *next_change,
972  GFunc unpack_func)
973 {
974  GList *pairs = make_pairs(top, xml_obj, set_name, always_first);
975 
976  if (pairs) {
977  unpack_data_t data = {
978  .hash = hash,
979  .node_hash = node_hash,
980  .now = now,
981  .overwrite = overwrite,
982  .next_change = next_change,
983  .top = top,
984  };
985 
986  g_list_foreach(pairs, unpack_func, &data);
987  g_list_free_full(pairs, free);
988  }
989 }
990 
1004 void
1005 pe_unpack_nvpairs(xmlNode *top, xmlNode *xml_obj, const char *set_name,
1006  GHashTable *node_hash, GHashTable *hash,
1007  const char *always_first, gboolean overwrite,
1008  crm_time_t *now, crm_time_t *next_change)
1009 {
1010  unpack_nvpair_blocks(top, xml_obj, set_name, node_hash, hash, always_first,
1011  overwrite, now, next_change, unpack_attr_set);
1012 }
1013 
1014 #if ENABLE_VERSIONED_ATTRS
1015 void
1016 pe_unpack_versioned_attributes(xmlNode *top, xmlNode *xml_obj,
1017  const char *set_name, GHashTable *node_hash,
1018  xmlNode *hash, crm_time_t *now,
1019  crm_time_t *next_change)
1020 {
1021  unpack_nvpair_blocks(top, xml_obj, set_name, node_hash, hash, NULL, FALSE,
1022  now, next_change, unpack_versioned_attr_set);
1023 }
1024 #endif
1025 
1026 char *
1027 pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
1028 {
1029  size_t len = 0;
1030  int i;
1031  const char *p, *last_match_index;
1032  char *p_dst, *result = NULL;
1033 
1034  if (pcmk__str_empty(string) || !match_data) {
1035  return NULL;
1036  }
1037 
1038  p = last_match_index = string;
1039 
1040  while (*p) {
1041  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
1042  i = *(p + 1) - '0';
1043  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
1044  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
1045  len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
1046  last_match_index = p + 2;
1047  }
1048  p++;
1049  }
1050  p++;
1051  }
1052  len += p - last_match_index + 1;
1053 
1054  /* FIXME: Excessive? */
1055  if (len - 1 <= 0) {
1056  return NULL;
1057  }
1058 
1059  p_dst = result = calloc(1, len);
1060  p = string;
1061 
1062  while (*p) {
1063  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
1064  i = *(p + 1) - '0';
1065  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
1066  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
1067  /* rm_eo can be equal to rm_so, but then there is nothing to do */
1068  int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
1069  memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
1070  p_dst += match_len;
1071  }
1072  p++;
1073  } else {
1074  *(p_dst) = *(p);
1075  p_dst++;
1076  }
1077  p++;
1078  }
1079 
1080  return result;
1081 }
1082 
1083 #if ENABLE_VERSIONED_ATTRS
1084 GHashTable*
1085 pe_unpack_versioned_parameters(xmlNode *versioned_params, const char *ra_version)
1086 {
1087  GHashTable *hash = crm_str_table_new();
1088 
1089  if (versioned_params && ra_version) {
1090  GHashTable *node_hash = crm_str_table_new();
1091  xmlNode *attr_set = __xml_first_child_element(versioned_params);
1092 
1093  if (attr_set) {
1094  g_hash_table_insert(node_hash, strdup(CRM_ATTR_RA_VERSION),
1095  strdup(ra_version));
1096  pe_unpack_nvpairs(NULL, versioned_params,
1097  crm_element_name(attr_set), node_hash, hash, NULL,
1098  FALSE, NULL, NULL);
1099  }
1100 
1101  g_hash_table_destroy(node_hash);
1102  }
1103 
1104  return hash;
1105 }
1106 #endif
1107 
1108 // Deprecated functions kept only for backward API compatibility
1109 gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now);
1110 gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
1111  crm_time_t *now);
1112 gboolean pe_test_rule_re(xmlNode *rule, GHashTable *node_hash,
1113  enum rsc_role_e role, crm_time_t *now,
1114  pe_re_match_data_t *re_match_data);
1115 gboolean pe_test_rule_full(xmlNode *rule, GHashTable *node_hash,
1116  enum rsc_role_e role, crm_time_t *now,
1117  pe_match_data_t *match_data);
1118 gboolean test_expression(xmlNode *expr, GHashTable *node_hash,
1119  enum rsc_role_e role, crm_time_t *now);
1120 gboolean pe_test_expression_re(xmlNode *expr, GHashTable *node_hash,
1121  enum rsc_role_e role, crm_time_t *now,
1122  pe_re_match_data_t *re_match_data);
1123 gboolean pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
1124  enum rsc_role_e role, crm_time_t *now,
1125  pe_match_data_t *match_data);
1126 void unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj,
1127  const char *set_name, GHashTable *node_hash,
1128  GHashTable *hash, const char *always_first,
1129  gboolean overwrite, crm_time_t *now);
1130 
1131 gboolean
1132 test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
1133 {
1134  return pe_evaluate_rules(ruleset, node_hash, now, NULL);
1135 }
1136 
1137 gboolean
1138 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1139 {
1140  return pe_test_rule(rule, node_hash, role, now, NULL, NULL);
1141 }
1142 
1143 gboolean
1144 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)
1145 {
1146  pe_match_data_t match_data = {
1147  .re = re_match_data,
1148  .params = NULL,
1149  .meta = NULL,
1150  };
1151  return pe_test_rule(rule, node_hash, role, now, NULL, &match_data);
1152 }
1153 
1154 gboolean
1155 pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
1156  crm_time_t *now, pe_match_data_t *match_data)
1157 {
1158  return pe_test_rule(rule, node_hash, role, now, NULL, match_data);
1159 }
1160 
1161 gboolean
1162 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
1163 {
1164  return pe_test_expression(expr, node_hash, role, now, NULL, NULL);
1165 }
1166 
1167 gboolean
1168 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)
1169 {
1170  pe_match_data_t match_data = {
1171  .re = re_match_data,
1172  .params = NULL,
1173  .meta = NULL,
1174  };
1175  return pe_test_expression(expr, node_hash, role, now, NULL, &match_data);
1176 }
1177 
1178 gboolean
1179 pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
1180  enum rsc_role_e role, crm_time_t *now,
1181  pe_match_data_t *match_data)
1182 {
1183  return pe_test_expression(expr, node_hash, role, now, NULL, match_data);
1184 }
1185 
1186 void
1187 unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
1188  GHashTable *node_hash, GHashTable *hash,
1189  const char *always_first, gboolean overwrite,
1190  crm_time_t *now)
1191 {
1192  unpack_nvpair_blocks(top, xml_obj, set_name, node_hash, hash, always_first,
1193  overwrite, now, NULL, unpack_attr_set);
1194 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:233
char uname[MAX_NAME]
Definition: internal.h:85
A dumping ground.
int pe_cron_range_satisfied(crm_time_t *now, xmlNode *cron_spec)
Definition: rules.c:513
void crm_time_add_years(crm_time_t *dt, int value)
Definition: iso8601.c:1522
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
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:263
#define CRM_ATTR_KIND
Definition: crm.h:112
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:1005
#define XML_EXPR_ATTR_TYPE
Definition: msg_xml.h:305
bool crm_time_is_defined(const crm_time_t *t)
Check whether a time object has been initialized yet.
Definition: iso8601.c:133
struct crm_time_s crm_time_t
Definition: iso8601.h:32
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:63
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:296
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:1168
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:4399
int char2score(const char *score)
Definition: utils.c:59
pe_re_match_data_t * re
Definition: rules.h:41
#define XML_TAG_ATTRS
Definition: msg_xml.h:165
#define CRM_ATTR_ROLE
Definition: crm.h:113
#define XML_EXPR_ATTR_VALUE_SOURCE
Definition: msg_xml.h:306
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:126
int crm_time_get_ordinal(crm_time_t *dt, uint32_t *y, uint32_t *d)
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:339
#define CHECK_ONE(spec, name, var)
Definition: rules.c:505
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:124
#define CRM_ATTR_RA_VERSION
Definition: crm.h:120
gboolean pe_test_role_expression(xmlNode *expr, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:216
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:160
void crm_time_add_hours(crm_time_t *dt, int value)
Definition: iso8601.c:1510
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:181
#define CRM_TRACE_INIT_DATA(name)
Definition: logging.h:134
int pe_eval_date_expression(xmlNode *time_expr, crm_time_t *now, crm_time_t *next_change)
Definition: rules.c:620
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2142
gboolean pe_test_date_expression(xmlNode *time_expr, crm_time_t *now, crm_time_t *next_change)
Definition: rules.c:584
int rc
Definition: pcmk_fence.c:34
Definition: rules.h:25
#define crm_debug(fmt, args...)
Definition: logging.h:368
crm_time_t * pe_parse_xml_duration(crm_time_t *start, xmlNode *duration_spec)
Definition: rules.c:554
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:522
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:1155
#define XML_EXPR_ATTR_VALUE
Definition: msg_xml.h:304
void crm_time_add_months(crm_time_t *dt, int value)
Definition: iso8601.c:1462
#define CRM_ATTR_UNAME
Definition: crm.h:110
#define crm_trace(fmt, args...)
Definition: logging.h:369
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:4472
Wrappers for and extensions to libxml2.
void crm_time_add_minutes(crm_time_t *dt, int value)
Definition: iso8601.c:1504
#define XML_EXPR_ATTR_OPERATION
Definition: msg_xml.h:303
GHashTable * meta
Definition: rules.h:43
char * pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
Definition: rules.c:1027
void free_xml(xmlNode *child)
Definition: xml.c:2136
enum rsc_role_e text2role(const char *role)
Definition: common.c:484
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:326
#define XML_RULE_ATTR_BOOLEAN_OP
Definition: msg_xml.h:299
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
gboolean pe_test_attr_expression(xmlNode *expr, GHashTable *hash, crm_time_t *now, pe_match_data_t *match_data)
Definition: rules.c:257
struct unpack_data_s unpack_data_t
GHashTable * params
Definition: rules.h:42
struct sorted_set_s sorted_set_t
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition: rules.c:1132
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:363
int crm_time_get_timeofday(crm_time_t *dt, uint32_t *h, uint32_t *m, uint32_t *s)
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:1179
void crm_time_set(crm_time_t *target, crm_time_t *source)
Definition: iso8601.c:1193
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:93
regmatch_t * pmatch
Definition: rules.h:37
int compare_version(const char *version1, const char *version2)
Definition: utils.c:227
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:340
char data[0]
Definition: internal.h:90
#define crm_str(x)
Definition: logging.h:389
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:38
int crm_time_compare(crm_time_t *dt, crm_time_t *rhs)
Definition: iso8601.c:1387
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:1144
rsc_role_e
Definition: common.h:76
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:1968
Definition: rules.h:28
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:1187
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1138
expression_type
Definition: rules.h:24
#define ID(x)
Definition: msg_xml.h:418
#define pe_err(fmt...)
Definition: internal.h:21
#define safe_str_eq(a, b)
Definition: util.h:65
char * name
Definition: pcmk_fence.c:30
#define XML_TAG_RULE
Definition: msg_xml.h:295
#define CRM_ATTR_ID
Definition: crm.h:111
GList * GListPtr
Definition: crm.h:214
char int pcmk__parse_ll_range(const char *srcstring, long long *start, long long *end)
Definition: strings.c:605
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:302
void crm_time_add_days(crm_time_t *dt, int value)
Definition: iso8601.c:1437
char * string
Definition: rules.h:35
#define update_field(xml_field, time_fn)
Definition: rules.c:546
enum crm_ais_msg_types type
Definition: internal.h:83
xmlNode * crm_next_same_xml(const xmlNode *sibling)
Get next instance of same XML tag.
Definition: xml.c:4425
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:141
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:1162