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