pacemaker  2.0.1-9e909a5bdd
Scalable High-Availability cluster resource manager
rules.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  */
18 
19 #include <crm_internal.h>
20 #include <crm/crm.h>
21 #include <crm/msg_xml.h>
22 #include <crm/common/xml.h>
23 
24 #include <glib.h>
25 
26 #include <crm/pengine/rules.h>
27 #include <crm/pengine/internal.h>
28 
29 #include <sys/types.h>
30 #include <regex.h>
31 #include <ctype.h>
32 
33 CRM_TRACE_INIT_DATA(pe_rules);
34 
35 crm_time_t *parse_xml_duration(crm_time_t * start, xmlNode * duration_spec);
36 
37 gboolean test_date_expression(xmlNode * time_expr, crm_time_t * now);
38 gboolean cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec);
39 gboolean test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now);
40 gboolean pe_test_attr_expression_full(xmlNode * expr, GHashTable * hash, crm_time_t * now, pe_match_data_t * match_data);
41 gboolean test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now);
42 
43 gboolean
44 test_ruleset(xmlNode * ruleset, GHashTable * node_hash, crm_time_t * now)
45 {
46  gboolean ruleset_default = TRUE;
47  xmlNode *rule = NULL;
48 
49  for (rule = __xml_first_child(ruleset); rule != NULL; rule = __xml_next_element(rule)) {
50  if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
51  ruleset_default = FALSE;
52  if (test_rule(rule, node_hash, RSC_ROLE_UNKNOWN, now)) {
53  return TRUE;
54  }
55  }
56  }
57 
58  return ruleset_default;
59 }
60 
61 gboolean
62 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
63 {
64  return pe_test_rule_full(rule, node_hash, role, now, NULL);
65 }
66 
67 gboolean
68 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)
69 {
70  pe_match_data_t match_data = {
71  .re = re_match_data,
72  .params = NULL,
73  .meta = NULL,
74  };
75  return pe_test_rule_full(rule, node_hash, role, now, &match_data);
76 }
77 
78 gboolean
79 pe_test_rule_full(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_match_data_t * match_data)
80 {
81  xmlNode *expr = NULL;
82  gboolean test = TRUE;
83  gboolean empty = TRUE;
84  gboolean passed = TRUE;
85  gboolean do_and = TRUE;
86  const char *value = NULL;
87 
88  rule = expand_idref(rule, NULL);
90  if (safe_str_eq(value, "or")) {
91  do_and = FALSE;
92  passed = FALSE;
93  }
94 
95  crm_trace("Testing rule %s", ID(rule));
96  for (expr = __xml_first_child(rule); expr != NULL; expr = __xml_next_element(expr)) {
97  test = pe_test_expression_full(expr, node_hash, role, now, match_data);
98  empty = FALSE;
99 
100  if (test && do_and == FALSE) {
101  crm_trace("Expression %s/%s passed", ID(rule), ID(expr));
102  return TRUE;
103 
104  } else if (test == FALSE && do_and) {
105  crm_trace("Expression %s/%s failed", ID(rule), ID(expr));
106  return FALSE;
107  }
108  }
109 
110  if (empty) {
111  crm_err("Invalid Rule %s: rules must contain at least one expression", ID(rule));
112  }
113 
114  crm_trace("Rule %s %s", ID(rule), passed ? "passed" : "failed");
115  return passed;
116 }
117 
118 gboolean
119 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
120 {
121  return pe_test_expression_full(expr, node_hash, role, now, NULL);
122 }
123 
124 gboolean
125 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)
126 {
127  pe_match_data_t match_data = {
128  .re = re_match_data,
129  .params = NULL,
130  .meta = NULL,
131  };
132  return pe_test_expression_full(expr, node_hash, role, now, &match_data);
133 }
134 
135 gboolean
136 pe_test_expression_full(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now, pe_match_data_t * match_data)
137 {
138  gboolean accept = FALSE;
139  const char *uname = NULL;
140 
141  switch (find_expression_type(expr)) {
142  case nested_rule:
143  accept = pe_test_rule_full(expr, node_hash, role, now, match_data);
144  break;
145  case attr_expr:
146  case loc_expr:
147  /* these expressions can never succeed if there is
148  * no node to compare with
149  */
150  if (node_hash != NULL) {
151  accept = pe_test_attr_expression_full(expr, node_hash, now, match_data);
152  }
153  break;
154 
155  case time_expr:
156  accept = test_date_expression(expr, now);
157  break;
158 
159  case role_expr:
160  accept = test_role_expression(expr, role, now);
161  break;
162 
163 #ifdef ENABLE_VERSIONED_ATTRS
164  case version_expr:
165  if (node_hash && g_hash_table_lookup_extended(node_hash,
167  NULL, NULL)) {
168  accept = test_attr_expression(expr, node_hash, now);
169  } else {
170  // we are going to test it when we have ra-version
171  accept = TRUE;
172  }
173  break;
174 #endif
175 
176  default:
177  CRM_CHECK(FALSE /* bad type */ , return FALSE);
178  accept = FALSE;
179  }
180  if (node_hash) {
181  uname = g_hash_table_lookup(node_hash, CRM_ATTR_UNAME);
182  }
183 
184  crm_trace("Expression %s %s on %s",
185  ID(expr), accept ? "passed" : "failed", uname ? uname : "all nodes");
186  return accept;
187 }
188 
189 enum expression_type
190 find_expression_type(xmlNode * expr)
191 {
192  const char *tag = NULL;
193  const char *attr = NULL;
194 
196  tag = crm_element_name(expr);
197 
198  if (safe_str_eq(tag, "date_expression")) {
199  return time_expr;
200 
201  } else if (safe_str_eq(tag, XML_TAG_RULE)) {
202  return nested_rule;
203 
204  } else if (safe_str_neq(tag, "expression")) {
205  return not_expr;
206 
207  } else if (safe_str_eq(attr, CRM_ATTR_UNAME)
208  || safe_str_eq(attr, CRM_ATTR_KIND)
209  || safe_str_eq(attr, CRM_ATTR_ID)) {
210  return loc_expr;
211 
212  } else if (safe_str_eq(attr, CRM_ATTR_ROLE)) {
213  return role_expr;
214 
215 #ifdef ENABLE_VERSIONED_ATTRS
216  } else if (safe_str_eq(attr, CRM_ATTR_RA_VERSION)) {
217  return version_expr;
218 #endif
219  }
220 
221  return attr_expr;
222 }
223 
224 gboolean
225 test_role_expression(xmlNode * expr, enum rsc_role_e role, crm_time_t * now)
226 {
227  gboolean accept = FALSE;
228  const char *op = NULL;
229  const char *value = NULL;
230 
231  if (role == RSC_ROLE_UNKNOWN) {
232  return accept;
233  }
234 
235  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
237 
238  if (safe_str_eq(op, "defined")) {
239  if (role > RSC_ROLE_STARTED) {
240  accept = TRUE;
241  }
242 
243  } else if (safe_str_eq(op, "not_defined")) {
244  if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
245  accept = TRUE;
246  }
247 
248  } else if (safe_str_eq(op, "eq")) {
249  if (text2role(value) == role) {
250  accept = TRUE;
251  }
252 
253  } else if (safe_str_eq(op, "ne")) {
254  // Test "ne" only with promotable clone roles
255  if (role < RSC_ROLE_SLAVE && role > RSC_ROLE_UNKNOWN) {
256  accept = FALSE;
257 
258  } else if (text2role(value) != role) {
259  accept = TRUE;
260  }
261  }
262  return accept;
263 }
264 
265 gboolean
266 test_attr_expression(xmlNode * expr, GHashTable * hash, crm_time_t * now)
267 {
268  return pe_test_attr_expression_full(expr, hash, now, NULL);
269 }
270 
271 gboolean
272 pe_test_attr_expression_full(xmlNode * expr, GHashTable * hash, crm_time_t * now, pe_match_data_t * match_data)
273 {
274  gboolean accept = FALSE;
275  gboolean attr_allocated = FALSE;
276  int cmp = 0;
277  const char *h_val = NULL;
278  GHashTable *table = NULL;
279 
280  const char *op = NULL;
281  const char *type = NULL;
282  const char *attr = NULL;
283  const char *value = NULL;
284  const char *value_source = NULL;
285 
288  value = crm_element_value(expr, XML_EXPR_ATTR_VALUE);
290  value_source = crm_element_value(expr, XML_EXPR_ATTR_VALUE_SOURCE);
291 
292  if (attr == NULL || op == NULL) {
293  pe_err("Invalid attribute or operation in expression"
294  " (\'%s\' \'%s\' \'%s\')", crm_str(attr), crm_str(op), crm_str(value));
295  return FALSE;
296  }
297 
298  if (match_data) {
299  if (match_data->re) {
300  char *resolved_attr = pe_expand_re_matches(attr, match_data->re);
301 
302  if (resolved_attr) {
303  attr = (const char *) resolved_attr;
304  attr_allocated = TRUE;
305  }
306  }
307 
308  if (safe_str_eq(value_source, "param")) {
309  table = match_data->params;
310  } else if (safe_str_eq(value_source, "meta")) {
311  table = match_data->meta;
312  }
313  }
314 
315  if (table) {
316  const char *param_name = value;
317  const char *param_value = NULL;
318 
319  if (param_name && param_name[0]) {
320  if ((param_value = (const char *)g_hash_table_lookup(table, param_name))) {
321  value = param_value;
322  }
323  }
324  }
325 
326  if (hash != NULL) {
327  h_val = (const char *)g_hash_table_lookup(hash, attr);
328  }
329 
330  if (attr_allocated) {
331  free((char *)attr);
332  attr = NULL;
333  }
334 
335  if (value != NULL && h_val != NULL) {
336  if (type == NULL) {
337  if (safe_str_eq(op, "lt")
338  || safe_str_eq(op, "lte")
339  || safe_str_eq(op, "gt")
340  || safe_str_eq(op, "gte")) {
341  type = "number";
342 
343  } else {
344  type = "string";
345  }
346  crm_trace("Defaulting to %s based comparison for '%s' op", type, op);
347  }
348 
349  if (safe_str_eq(type, "string")) {
350  cmp = strcasecmp(h_val, value);
351 
352  } else if (safe_str_eq(type, "number")) {
353  int h_val_f = crm_parse_int(h_val, NULL);
354  int value_f = crm_parse_int(value, NULL);
355 
356  if (h_val_f < value_f) {
357  cmp = -1;
358  } else if (h_val_f > value_f) {
359  cmp = 1;
360  } else {
361  cmp = 0;
362  }
363 
364  } else if (safe_str_eq(type, "version")) {
365  cmp = compare_version(h_val, value);
366 
367  }
368 
369  } else if (value == NULL && h_val == NULL) {
370  cmp = 0;
371  } else if (value == NULL) {
372  cmp = 1;
373  } else {
374  cmp = -1;
375  }
376 
377  if (safe_str_eq(op, "defined")) {
378  if (h_val != NULL) {
379  accept = TRUE;
380  }
381 
382  } else if (safe_str_eq(op, "not_defined")) {
383  if (h_val == NULL) {
384  accept = TRUE;
385  }
386 
387  } else if (safe_str_eq(op, "eq")) {
388  if ((h_val == value) || cmp == 0) {
389  accept = TRUE;
390  }
391 
392  } else if (safe_str_eq(op, "ne")) {
393  if ((h_val == NULL && value != NULL)
394  || (h_val != NULL && value == NULL)
395  || cmp != 0) {
396  accept = TRUE;
397  }
398 
399  } else if (value == NULL || h_val == NULL) {
400  // The comparison is meaningless from this point on
401  accept = FALSE;
402 
403  } else if (safe_str_eq(op, "lt")) {
404  if (cmp < 0) {
405  accept = TRUE;
406  }
407 
408  } else if (safe_str_eq(op, "lte")) {
409  if (cmp <= 0) {
410  accept = TRUE;
411  }
412 
413  } else if (safe_str_eq(op, "gt")) {
414  if (cmp > 0) {
415  accept = TRUE;
416  }
417 
418  } else if (safe_str_eq(op, "gte")) {
419  if (cmp >= 0) {
420  accept = TRUE;
421  }
422  }
423 
424  return accept;
425 }
426 
427 /* As per the nethack rules:
428  *
429  * moon period = 29.53058 days ~= 30, year = 365.2422 days
430  * days moon phase advances on first day of year compared to preceding year
431  * = 365.2422 - 12*29.53058 ~= 11
432  * years in Metonic cycle (time until same phases fall on the same days of
433  * the month) = 18.6 ~= 19
434  * moon phase on first day of year (epact) ~= (11*(year%19) + 29) % 30
435  * (29 as initial condition)
436  * current phase in days = first day phase + days elapsed in year
437  * 6 moons ~= 177 days
438  * 177 ~= 8 reported phases * 22
439  * + 11/22 for rounding
440  *
441  * 0-7, with 0: new, 4: full
442  */
443 
444 static int
445 phase_of_the_moon(crm_time_t * now)
446 {
447  uint32_t epact, diy, goldn;
448  uint32_t y;
449 
450  crm_time_get_ordinal(now, &y, &diy);
451 
452  goldn = (y % 19) + 1;
453  epact = (11 * goldn + 18) % 30;
454  if ((epact == 25 && goldn > 11) || epact == 24)
455  epact++;
456 
457  return ((((((diy + epact) * 6) + 11) % 177) / 22) & 7);
458 }
459 
460 static gboolean
461 decodeNVpair(const char *srcstring, char separator, char **name, char **value)
462 {
463  const char *seploc = NULL;
464 
465  CRM_ASSERT(name != NULL && value != NULL);
466  *name = NULL;
467  *value = NULL;
468 
469  crm_trace("Attempting to decode: [%s]", srcstring);
470  if (srcstring != NULL) {
471  seploc = strchr(srcstring, separator);
472  if (seploc) {
473  *name = strndup(srcstring, seploc - srcstring);
474  if (*(seploc + 1)) {
475  *value = strdup(seploc + 1);
476  }
477  return TRUE;
478  }
479  }
480  return FALSE;
481 }
482 
483 #define cron_check(xml_field, time_field) \
484  value = crm_element_value(cron_spec, xml_field); \
485  if(value != NULL) { \
486  gboolean pass = TRUE; \
487  decodeNVpair(value, '-', &value_low, &value_high); \
488  if(value_low == NULL) { \
489  value_low = strdup(value); \
490  } \
491  value_low_i = crm_parse_int(value_low, "0"); \
492  value_high_i = crm_parse_int(value_high, "-1"); \
493  if(value_high_i < 0) { \
494  if(value_low_i != time_field) { \
495  pass = FALSE; \
496  } \
497  } else if(value_low_i > time_field) { \
498  pass = FALSE; \
499  } else if(value_high_i < time_field) { \
500  pass = FALSE; \
501  } \
502  free(value_low); \
503  free(value_high); \
504  if(pass == FALSE) { \
505  crm_debug("Condition '%s' in %s: failed", value, xml_field); \
506  return pass; \
507  } \
508  crm_debug("Condition '%s' in %s: passed", value, xml_field); \
509  }
510 
511 gboolean
512 cron_range_satisfied(crm_time_t * now, xmlNode * cron_spec)
513 {
514  const char *value = NULL;
515  char *value_low = NULL;
516  char *value_high = NULL;
517 
518  int value_low_i = 0;
519  int value_high_i = 0;
520 
521  uint32_t h, m, s, y, d, w;
522 
523  CRM_CHECK(now != NULL, return FALSE);
524 
525  crm_time_get_timeofday(now, &h, &m, &s);
526 
527  cron_check("seconds", s);
528  cron_check("minutes", m);
529  cron_check("hours", h);
530 
531  crm_time_get_gregorian(now, &y, &m, &d);
532 
533  cron_check("monthdays", d);
534  cron_check("months", m);
535  cron_check("years", y);
536 
537  crm_time_get_ordinal(now, &y, &d);
538 
539  cron_check("yeardays", d);
540 
541  crm_time_get_isoweek(now, &y, &w, &d);
542 
543  cron_check("weekyears", y);
544  cron_check("weeks", w);
545  cron_check("weekdays", d);
546 
547  cron_check("moon", phase_of_the_moon(now));
548 
549  return TRUE;
550 }
551 
552 #define update_field(xml_field, time_fn) \
553  value = crm_element_value(duration_spec, xml_field); \
554  if(value != NULL) { \
555  int value_i = crm_parse_int(value, "0"); \
556  time_fn(end, value_i); \
557  }
558 
559 crm_time_t *
560 parse_xml_duration(crm_time_t * start, xmlNode * duration_spec)
561 {
562  crm_time_t *end = NULL;
563  const char *value = NULL;
564 
565  end = crm_time_new(NULL);
566  crm_time_set(end, start);
567 
575 
576  return end;
577 }
578 
579 gboolean
581 {
582  crm_time_t *start = NULL;
583  crm_time_t *end = NULL;
584  const char *value = NULL;
585  const char *op = crm_element_value(time_expr, "operation");
586 
587  xmlNode *duration_spec = NULL;
588  xmlNode *date_spec = NULL;
589 
590  gboolean passed = FALSE;
591 
592  crm_trace("Testing expression: %s", ID(time_expr));
593 
594  duration_spec = first_named_child(time_expr, "duration");
595  date_spec = first_named_child(time_expr, "date_spec");
596 
597  value = crm_element_value(time_expr, "start");
598  if (value != NULL) {
599  start = crm_time_new(value);
600  }
601  value = crm_element_value(time_expr, "end");
602  if (value != NULL) {
603  end = crm_time_new(value);
604  }
605 
606  if (start != NULL && end == NULL && duration_spec != NULL) {
607  end = parse_xml_duration(start, duration_spec);
608  }
609  if (op == NULL) {
610  op = "in_range";
611  }
612 
613  if (safe_str_eq(op, "date_spec") || safe_str_eq(op, "in_range")) {
614  if (start != NULL && crm_time_compare(start, now) > 0) {
615  passed = FALSE;
616  } else if (end != NULL && crm_time_compare(end, now) < 0) {
617  passed = FALSE;
618  } else if (safe_str_eq(op, "in_range")) {
619  passed = TRUE;
620  } else {
621  passed = cron_range_satisfied(now, date_spec);
622  }
623 
624  } else if (safe_str_eq(op, "gt") && crm_time_compare(start, now) < 0) {
625  passed = TRUE;
626 
627  } else if (safe_str_eq(op, "lt") && crm_time_compare(end, now) > 0) {
628  passed = TRUE;
629 
630  } else if (safe_str_eq(op, "eq") && crm_time_compare(start, now) == 0) {
631  passed = TRUE;
632 
633  } else if (safe_str_eq(op, "neq") && crm_time_compare(start, now) != 0) {
634  passed = TRUE;
635  }
636 
637  crm_time_free(start);
638  crm_time_free(end);
639  return passed;
640 }
641 
642 typedef struct sorted_set_s {
643  int score;
644  const char *name;
645  const char *special_name;
646  xmlNode *attr_set;
647 } sorted_set_t;
648 
649 static gint
650 sort_pairs(gconstpointer a, gconstpointer b)
651 {
652  const sorted_set_t *pair_a = a;
653  const sorted_set_t *pair_b = b;
654 
655  if (a == NULL && b == NULL) {
656  return 0;
657  } else if (a == NULL) {
658  return 1;
659  } else if (b == NULL) {
660  return -1;
661  }
662 
663  if (safe_str_eq(pair_a->name, pair_a->special_name)) {
664  return -1;
665 
666  } else if (safe_str_eq(pair_b->name, pair_a->special_name)) {
667  return 1;
668  }
669 
670  if (pair_a->score < pair_b->score) {
671  return 1;
672  } else if (pair_a->score > pair_b->score) {
673  return -1;
674  }
675  return 0;
676 }
677 
678 static void
679 populate_hash(xmlNode * nvpair_list, GHashTable * hash, gboolean overwrite, xmlNode * top)
680 {
681  const char *name = NULL;
682  const char *value = NULL;
683  const char *old_value = NULL;
684  xmlNode *list = nvpair_list;
685  xmlNode *an_attr = NULL;
686 
687  name = crm_element_name(list->children);
688  if (safe_str_eq(XML_TAG_ATTRS, name)) {
689  list = list->children;
690  }
691 
692  for (an_attr = __xml_first_child(list); an_attr != NULL; an_attr = __xml_next_element(an_attr)) {
693  if (crm_str_eq((const char *)an_attr->name, XML_CIB_TAG_NVPAIR, TRUE)) {
694  xmlNode *ref_nvpair = expand_idref(an_attr, top);
695 
696  name = crm_element_value(an_attr, XML_NVPAIR_ATTR_NAME);
697  if (name == NULL) {
698  name = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_NAME);
699  }
700 
701  crm_trace("Setting attribute: %s", name);
702  value = crm_element_value(an_attr, XML_NVPAIR_ATTR_VALUE);
703  if (value == NULL) {
704  value = crm_element_value(ref_nvpair, XML_NVPAIR_ATTR_VALUE);
705  }
706 
707  if (name == NULL || value == NULL) {
708  continue;
709 
710  }
711 
712  old_value = g_hash_table_lookup(hash, name);
713 
714  if (safe_str_eq(value, "#default")) {
715  if (old_value) {
716  crm_trace("Removing value for %s (%s)", name, value);
717  g_hash_table_remove(hash, name);
718  }
719  continue;
720 
721  } else if (old_value == NULL) {
722  g_hash_table_insert(hash, strdup(name), strdup(value));
723 
724  } else if (overwrite) {
725  crm_debug("Overwriting value of %s: %s -> %s", name, old_value, value);
726  g_hash_table_replace(hash, strdup(name), strdup(value));
727  }
728  }
729  }
730 }
731 
732 #ifdef ENABLE_VERSIONED_ATTRS
733 static xmlNode*
734 get_versioned_rule(xmlNode * attr_set)
735 {
736  xmlNode * rule = NULL;
737  xmlNode * expr = NULL;
738 
739  for (rule = __xml_first_child(attr_set); rule != NULL; rule = __xml_next_element(rule)) {
740  if (crm_str_eq((const char *)rule->name, XML_TAG_RULE, TRUE)) {
741  for (expr = __xml_first_child(rule); expr != NULL; expr = __xml_next_element(expr)) {
742  if (find_expression_type(expr) == version_expr) {
743  return rule;
744  }
745  }
746  }
747  }
748 
749  return NULL;
750 }
751 
752 static void
753 add_versioned_attributes(xmlNode * attr_set, xmlNode * versioned_attrs)
754 {
755  xmlNode *attr_set_copy = NULL;
756  xmlNode *rule = NULL;
757  xmlNode *expr = NULL;
758 
759  if (!attr_set || !versioned_attrs) {
760  return;
761  }
762 
763  attr_set_copy = copy_xml(attr_set);
764 
765  rule = get_versioned_rule(attr_set_copy);
766  if (!rule) {
767  free_xml(attr_set_copy);
768  return;
769  }
770 
771  expr = __xml_first_child(rule);
772  while (expr != NULL) {
773  if (find_expression_type(expr) != version_expr) {
774  xmlNode *node = expr;
775 
776  expr = __xml_next_element(expr);
777  free_xml(node);
778  } else {
779  expr = __xml_next_element(expr);
780  }
781  }
782 
783  add_node_nocopy(versioned_attrs, NULL, attr_set_copy);
784 }
785 #endif
786 
787 typedef struct unpack_data_s {
788  gboolean overwrite;
789  GHashTable *node_hash;
790  void *hash;
791  crm_time_t *now;
792  xmlNode *top;
793 } unpack_data_t;
794 
795 static void
796 unpack_attr_set(gpointer data, gpointer user_data)
797 {
798  sorted_set_t *pair = data;
799  unpack_data_t *unpack_data = user_data;
800 
801  if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
802  return;
803  }
804 
805 #ifdef ENABLE_VERSIONED_ATTRS
806  if (get_versioned_rule(pair->attr_set) && !(unpack_data->node_hash &&
807  g_hash_table_lookup_extended(unpack_data->node_hash,
808  CRM_ATTR_RA_VERSION, NULL, NULL))) {
809  // we haven't actually tested versioned expressions yet
810  return;
811  }
812 #endif
813 
814  crm_trace("Adding attributes from %s", pair->name);
815  populate_hash(pair->attr_set, unpack_data->hash, unpack_data->overwrite, unpack_data->top);
816 }
817 
818 #ifdef ENABLE_VERSIONED_ATTRS
819 static void
820 unpack_versioned_attr_set(gpointer data, gpointer user_data)
821 {
822  sorted_set_t *pair = data;
823  unpack_data_t *unpack_data = user_data;
824 
825  if (test_ruleset(pair->attr_set, unpack_data->node_hash, unpack_data->now) == FALSE) {
826  return;
827  }
828 
829  add_versioned_attributes(pair->attr_set, unpack_data->hash);
830 }
831 #endif
832 
833 static GListPtr
834 make_pairs_and_populate_data(xmlNode * top, xmlNode * xml_obj, const char *set_name,
835  GHashTable * node_hash, void * hash, const char *always_first,
836  gboolean overwrite, crm_time_t * now, unpack_data_t * data)
837 {
838  GListPtr unsorted = NULL;
839  const char *score = NULL;
840  sorted_set_t *pair = NULL;
841  xmlNode *attr_set = NULL;
842 
843  if (xml_obj == NULL) {
844  crm_trace("No instance attributes");
845  return NULL;
846  }
847 
848  crm_trace("Checking for attributes");
849  for (attr_set = __xml_first_child(xml_obj); attr_set != NULL; attr_set = __xml_next_element(attr_set)) {
850  /* Uncertain if set_name == NULL check is strictly necessary here */
851  if (set_name == NULL || crm_str_eq((const char *)attr_set->name, set_name, TRUE)) {
852  pair = NULL;
853  attr_set = expand_idref(attr_set, top);
854  if (attr_set == NULL) {
855  continue;
856  }
857 
858  pair = calloc(1, sizeof(sorted_set_t));
859  pair->name = ID(attr_set);
860  pair->special_name = always_first;
861  pair->attr_set = attr_set;
862 
863  score = crm_element_value(attr_set, XML_RULE_ATTR_SCORE);
864  pair->score = char2score(score);
865 
866  unsorted = g_list_prepend(unsorted, pair);
867  }
868  }
869 
870  if (pair != NULL) {
871  data->hash = hash;
872  data->node_hash = node_hash;
873  data->now = now;
874  data->overwrite = overwrite;
875  data->top = top;
876  }
877 
878  if (unsorted) {
879  return g_list_sort(unsorted, sort_pairs);
880  }
881 
882  return NULL;
883 }
884 
885 void
886 unpack_instance_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
887  GHashTable * node_hash, GHashTable * hash, const char *always_first,
888  gboolean overwrite, crm_time_t * now)
889 {
891  GListPtr pairs = make_pairs_and_populate_data(top, xml_obj, set_name, node_hash, hash,
892  always_first, overwrite, now, &data);
893 
894  if (pairs) {
895  g_list_foreach(pairs, unpack_attr_set, &data);
896  g_list_free_full(pairs, free);
897  }
898 }
899 
900 #ifdef ENABLE_VERSIONED_ATTRS
901 void
902 pe_unpack_versioned_attributes(xmlNode * top, xmlNode * xml_obj, const char *set_name,
903  GHashTable * node_hash, xmlNode * hash, crm_time_t * now)
904 {
906  GListPtr pairs = make_pairs_and_populate_data(top, xml_obj, set_name, node_hash, hash,
907  NULL, FALSE, now, &data);
908 
909  if (pairs) {
910  g_list_foreach(pairs, unpack_versioned_attr_set, &data);
911  g_list_free_full(pairs, free);
912  }
913 }
914 #endif
915 
916 char *
917 pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
918 {
919  size_t len = 0;
920  int i;
921  const char *p, *last_match_index;
922  char *p_dst, *result = NULL;
923 
924  if (!string || string[0] == '\0' || !match_data) {
925  return NULL;
926  }
927 
928  p = last_match_index = string;
929 
930  while (*p) {
931  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
932  i = *(p + 1) - '0';
933  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
934  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
935  len += p - last_match_index + (match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so);
936  last_match_index = p + 2;
937  }
938  p++;
939  }
940  p++;
941  }
942  len += p - last_match_index + 1;
943 
944  /* FIXME: Excessive? */
945  if (len - 1 <= 0) {
946  return NULL;
947  }
948 
949  p_dst = result = calloc(1, len);
950  p = string;
951 
952  while (*p) {
953  if (*p == '%' && *(p + 1) && isdigit(*(p + 1))) {
954  i = *(p + 1) - '0';
955  if (match_data->nregs >= i && match_data->pmatch[i].rm_so != -1 &&
956  match_data->pmatch[i].rm_eo > match_data->pmatch[i].rm_so) {
957  /* rm_eo can be equal to rm_so, but then there is nothing to do */
958  int match_len = match_data->pmatch[i].rm_eo - match_data->pmatch[i].rm_so;
959  memcpy(p_dst, match_data->string + match_data->pmatch[i].rm_so, match_len);
960  p_dst += match_len;
961  }
962  p++;
963  } else {
964  *(p_dst) = *(p);
965  p_dst++;
966  }
967  p++;
968  }
969 
970  return result;
971 }
972 
973 #ifdef ENABLE_VERSIONED_ATTRS
974 GHashTable*
975 pe_unpack_versioned_parameters(xmlNode *versioned_params, const char *ra_version)
976 {
977  GHashTable *hash = crm_str_table_new();
978 
979  if (versioned_params && ra_version) {
980  GHashTable *node_hash = crm_str_table_new();
981  xmlNode *attr_set = __xml_first_child(versioned_params);
982 
983  if (attr_set) {
984  g_hash_table_insert(node_hash, strdup(CRM_ATTR_RA_VERSION),
985  strdup(ra_version));
986  unpack_instance_attributes(NULL, versioned_params, crm_element_name(attr_set),
987  node_hash, hash, NULL, FALSE, NULL);
988  }
989 
990  g_hash_table_destroy(node_hash);
991  }
992 
993  return hash;
994 }
995 #endif
pe_match_data::re
pe_re_match_data_t * re
Definition: rules.h:49
GListPtr
GList * GListPtr
Definition: crm.h:190
crm_time_add_days
void crm_time_add_days(crm_time_t *dt, int value)
Definition: iso8601.c:1187
CRM_ATTR_KIND
#define CRM_ATTR_KIND
Definition: crm.h:88
cron_range_satisfied
gboolean cron_range_satisfied(crm_time_t *now, xmlNode *cron_spec)
Definition: rules.c:512
pe_re_match_data::nregs
int nregs
Definition: rules.h:44
crm_str_eq
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: strings.c:204
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:136
add_node_nocopy
int add_node_nocopy(xmlNode *parent, const char *name, xmlNode *child)
Definition: xml.c:1880
test_date_expression
gboolean test_date_expression(xmlNode *time_expr, crm_time_t *now)
Definition: rules.c:580
msg_xml.h
crm_time_add_seconds
void crm_time_add_seconds(crm_time_t *dt, int value)
Definition: iso8601.c:1166
test_role_expression
gboolean test_role_expression(xmlNode *expr, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:225
test_expression
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:119
data
char data[0]
Definition: internal.h:90
pe_re_match_data::pmatch
regmatch_t * pmatch
Definition: rules.h:45
nested_rule
Definition: rules.h:34
CRM_ATTR_ROLE
#define CRM_ATTR_ROLE
Definition: crm.h:89
rsc_role_e
rsc_role_e
Definition: common.h:86
crm_time_add_hours
void crm_time_add_hours(crm_time_t *dt, int value)
Definition: iso8601.c:1260
XML_RULE_ATTR_SCORE
#define XML_RULE_ATTR_SCORE
Definition: msg_xml.h:294
CRM_CHECK
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:165
copy_xml
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:2018
unpack_data_t
struct unpack_data_s unpack_data_t
cron_check
#define cron_check(xml_field, time_field)
Definition: rules.c:483
pe_match_data
Definition: rules.h:48
CRM_ATTR_UNAME
#define CRM_ATTR_UNAME
Definition: crm.h:86
XML_EXPR_ATTR_TYPE
#define XML_EXPR_ATTR_TYPE
Definition: msg_xml.h:303
compare_version
int compare_version(const char *version1, const char *version2)
Definition: utils.c:453
type
enum crm_ais_msg_types type
Definition: internal.h:83
crm_err
#define crm_err(fmt, args...)
Definition: logging.h:249
internal.h
crm_trace
#define crm_trace(fmt, args...)
Definition: logging.h:255
safe_str_eq
#define safe_str_eq(a, b)
Definition: util.h:54
XML_NVPAIR_ATTR_VALUE
#define XML_NVPAIR_ATTR_VALUE
Definition: msg_xml.h:338
free_xml
void free_xml(xmlNode *child)
Definition: xml.c:2012
crm_time_get_ordinal
int crm_time_get_ordinal(crm_time_t *dt, uint32_t *y, uint32_t *d)
crm_time_add_months
void crm_time_add_months(crm_time_t *dt, int value)
Definition: iso8601.c:1212
test_ruleset
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition: rules.c:44
xml.h
Wrappers for and extensions to libxml2.
loc_expr
Definition: rules.h:36
XML_EXPR_ATTR_VALUE
#define XML_EXPR_ATTR_VALUE
Definition: msg_xml.h:302
pe_test_attr_expression_full
gboolean pe_test_attr_expression_full(xmlNode *expr, GHashTable *hash, crm_time_t *now, pe_match_data_t *match_data)
Definition: rules.c:272
CRM_ATTR_ID
#define CRM_ATTR_ID
Definition: crm.h:87
ID
#define ID(x)
Definition: msg_xml.h:412
first_named_child
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:4124
pe_match_data::meta
GHashTable * meta
Definition: rules.h:51
pe_err
#define pe_err(fmt...)
Definition: internal.h:18
CRM_TRACE_INIT_DATA
CRM_TRACE_INIT_DATA(pe_rules)
role_expr
Definition: rules.h:37
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:79
XML_EXPR_ATTR_ATTRIBUTE
#define XML_EXPR_ATTR_ATTRIBUTE
Definition: msg_xml.h:300
attr_expr
Definition: rules.h:35
pe_expand_re_matches
char * pe_expand_re_matches(const char *string, pe_re_match_data_t *match_data)
Definition: rules.c:917
uint32_t
#define uint32_t
Definition: stdint.in.h:158
uname
char uname[MAX_NAME]
Definition: internal.h:85
XML_EXPR_ATTR_OPERATION
#define XML_EXPR_ATTR_OPERATION
Definition: msg_xml.h:301
parse_xml_duration
crm_time_t * parse_xml_duration(crm_time_t *start, xmlNode *duration_spec)
Definition: rules.c:560
version_expr
Definition: rules.h:39
pe_re_match_data
Definition: rules.h:42
RSC_ROLE_UNKNOWN
Definition: common.h:87
crm_debug
#define crm_debug(fmt, args...)
Definition: logging.h:254
XML_TAG_ATTRS
#define XML_TAG_ATTRS
Definition: msg_xml.h:163
update_field
#define update_field(xml_field, time_fn)
Definition: rules.c:552
XML_EXPR_ATTR_VALUE_SOURCE
#define XML_EXPR_ATTR_VALUE_SOURCE
Definition: msg_xml.h:304
expand_idref
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:4196
not_expr
Definition: rules.h:33
crm_time_add_weeks
void crm_time_add_weeks(crm_time_t *dt, int value)
Definition: iso8601.c:1266
crm_element_value
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:360
pe_match_data::params
GHashTable * params
Definition: rules.h:50
crm_time_add_minutes
void crm_time_add_minutes(crm_time_t *dt, int value)
Definition: iso8601.c:1254
text2role
enum rsc_role_e text2role(const char *role)
Definition: common.c:350
find_expression_type
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:190
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:125
test_rule
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:62
crm_parse_int
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:107
safe_str_neq
gboolean safe_str_neq(const char *a, const char *b)
Definition: strings.c:141
crm_time_get_gregorian
int crm_time_get_gregorian(crm_time_t *dt, uint32_t *y, uint32_t *m, uint32_t *d)
crm_str
#define crm_str(x)
Definition: logging.h:275
char2score
int char2score(const char *score)
Definition: utils.c:197
XML_RULE_ATTR_BOOLEAN_OP
#define XML_RULE_ATTR_BOOLEAN_OP
Definition: msg_xml.h:297
crm_time_compare
int crm_time_compare(crm_time_t *dt, crm_time_t *rhs)
Definition: iso8601.c:1139
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:68
pe_re_match_data::string
char * string
Definition: rules.h:43
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:20
XML_CIB_TAG_NVPAIR
#define XML_CIB_TAG_NVPAIR
Definition: msg_xml.h:158
crm_time_new
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:88
CRM_ATTR_RA_VERSION
#define CRM_ATTR_RA_VERSION
Definition: crm.h:96
expression_type
expression_type
Definition: rules.h:32
crm_time_free
void crm_time_free(crm_time_t *dt)
Definition: iso8601.c:105
XML_TAG_RULE
#define XML_TAG_RULE
Definition: msg_xml.h:293
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:972
crm_time_get_isoweek
int crm_time_get_isoweek(crm_time_t *dt, uint32_t *y, uint32_t *w, uint32_t *d)
time_expr
Definition: rules.h:38
strndup
char * strndup(const char *str, size_t len)
test_attr_expression
gboolean test_attr_expression(xmlNode *expr, GHashTable *hash, crm_time_t *now)
Definition: rules.c:266
crm_internal.h
crm.h
A dumping ground.
XML_NVPAIR_ATTR_NAME
#define XML_NVPAIR_ATTR_NAME
Definition: msg_xml.h:337
crm_time_add_years
void crm_time_add_years(crm_time_t *dt, int value)
Definition: iso8601.c:1272
crm_time_t
struct crm_time_s crm_time_t
Definition: iso8601.h:41
sorted_set_t
struct sorted_set_s sorted_set_t
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:886