pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
rules.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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 
12 #include <glib.h>
13 
14 #include <crm/crm.h>
15 #include <crm/common/xml.h>
16 #include <crm/pengine/rules.h>
17 
22 #include <crm/pengine/internal.h>
24 
25 #include <sys/types.h>
26 #include <regex.h>
27 
28 CRM_TRACE_INIT_DATA(pe_rules);
29 
37 static void
38 map_rule_input(pcmk_rule_input_t *new, const pe_rule_eval_data_t *old)
39 {
40  if (old == NULL) {
41  return;
42  }
43  new->now = old->now;
44  new->node_attrs = old->node_hash;
45  if (old->rsc_data != NULL) {
46  new->rsc_standard = old->rsc_data->standard;
47  new->rsc_provider = old->rsc_data->provider;
48  new->rsc_agent = old->rsc_data->agent;
49  }
50  if (old->match_data != NULL) {
51  new->rsc_params = old->match_data->params;
52  new->rsc_meta = old->match_data->meta;
53  if (old->match_data->re != NULL) {
54  new->rsc_id = old->match_data->re->string;
55  new->rsc_id_submatches = old->match_data->re->pmatch;
56  new->rsc_id_nmatches = old->match_data->re->nregs;
57  }
58  }
59  if (old->op_data != NULL) {
60  new->op_name = old->op_data->op_name;
61  new->op_interval_ms = old->op_data->interval;
62  }
63 }
64 
65 static gint
66 sort_pairs(gconstpointer a, gconstpointer b, gpointer user_data)
67 {
68  const xmlNode *pair_a = a;
69  const xmlNode *pair_b = b;
70  pcmk__nvpair_unpack_t *unpack_data = user_data;
71 
72  const char *score = NULL;
73  int score_a = 0;
74  int score_b = 0;
75 
76  if (a == NULL && b == NULL) {
77  return 0;
78  } else if (a == NULL) {
79  return 1;
80  } else if (b == NULL) {
81  return -1;
82  }
83 
84  if (pcmk__str_eq(pcmk__xe_id(pair_a), unpack_data->first_id,
85  pcmk__str_none)) {
86  return -1;
87 
88  } else if (pcmk__str_eq(pcmk__xe_id(pair_b), unpack_data->first_id,
89  pcmk__str_none)) {
90  return 1;
91  }
92 
93  score = crm_element_value(pair_a, PCMK_XA_SCORE);
94  score_a = char2score(score);
95 
96  score = crm_element_value(pair_b, PCMK_XA_SCORE);
97  score_b = char2score(score);
98 
99  /* If we're overwriting values, we want lowest score first, so the highest
100  * score is processed last; if we're not overwriting values, we want highest
101  * score first, so nothing else overwrites it.
102  */
103  if (score_a < score_b) {
104  return unpack_data->overwrite? -1 : 1;
105  } else if (score_a > score_b) {
106  return unpack_data->overwrite? 1 : -1;
107  }
108  return 0;
109 }
110 
111 static void
112 populate_hash(xmlNode *nvpair_list, GHashTable *hash, bool overwrite)
113 {
114  const char *name = NULL;
115  const char *value = NULL;
116  const char *old_value = NULL;
117  xmlNode *list = nvpair_list;
118  xmlNode *an_attr = NULL;
119 
120  if (pcmk__xe_is(list->children, PCMK__XE_ATTRIBUTES)) {
121  list = list->children;
122  }
123 
124  for (an_attr = pcmk__xe_first_child(list, NULL, NULL, NULL);
125  an_attr != NULL; an_attr = pcmk__xe_next(an_attr)) {
126 
127  if (pcmk__xe_is(an_attr, PCMK_XE_NVPAIR)) {
128  xmlNode *ref_nvpair = expand_idref(an_attr, NULL);
129 
130  name = crm_element_value(an_attr, PCMK_XA_NAME);
131  if ((name == NULL) && (ref_nvpair != NULL)) {
132  name = crm_element_value(ref_nvpair, PCMK_XA_NAME);
133  }
134 
135  value = crm_element_value(an_attr, PCMK_XA_VALUE);
136  if ((value == NULL) && (ref_nvpair != NULL)) {
137  value = crm_element_value(ref_nvpair, PCMK_XA_VALUE);
138  }
139 
140  if (name == NULL || value == NULL) {
141  continue;
142  }
143 
144  old_value = g_hash_table_lookup(hash, name);
145 
146  if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
147  // @COMPAT Deprecated since 2.1.8
148  pcmk__config_warn("Support for setting meta-attributes (such "
149  "as %s) to the explicit value '#default' is "
150  "deprecated and will be removed in a future "
151  "release", name);
152  if (old_value) {
153  crm_trace("Letting %s default (removing explicit value \"%s\")",
154  name, value);
155  g_hash_table_remove(hash, name);
156  }
157  continue;
158 
159  } else if (old_value == NULL) {
160  crm_trace("Setting %s=\"%s\"", name, value);
161  pcmk__insert_dup(hash, name, value);
162 
163  } else if (overwrite) {
164  crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")",
165  name, value, old_value);
166  pcmk__insert_dup(hash, name, value);
167  }
168  }
169  }
170 }
171 
172 static void
173 unpack_attr_set(gpointer data, gpointer user_data)
174 {
175  xmlNode *pair = data;
176  pcmk__nvpair_unpack_t *unpack_data = user_data;
177 
178  if (pcmk__evaluate_rules(pair, &(unpack_data->rule_input),
179  unpack_data->next_change) != pcmk_rc_ok) {
180  return;
181  }
182 
183  crm_trace("Adding name/value pairs from %s %s overwrite",
184  pcmk__xe_id(pair), (unpack_data->overwrite? "with" : "without"));
185  populate_hash(pair, unpack_data->values, unpack_data->overwrite);
186 }
187 
197 static GList *
198 make_pairs(const xmlNode *xml_obj, const char *set_name)
199 {
200  GList *unsorted = NULL;
201 
202  if (xml_obj == NULL) {
203  return NULL;
204  }
205  for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
206  attr_set != NULL; attr_set = pcmk__xe_next(attr_set)) {
207 
208  if ((set_name == NULL) || pcmk__xe_is(attr_set, set_name)) {
209  xmlNode *expanded_attr_set = expand_idref(attr_set, NULL);
210 
211  if (expanded_attr_set == NULL) {
212  continue; // Not possible with schema validation enabled
213  }
214  unsorted = g_list_prepend(unsorted, expanded_attr_set);
215  }
216  }
217  return unsorted;
218 }
219 
232 void
233 pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
234  const pe_rule_eval_data_t *rule_data, GHashTable *hash,
235  const char *always_first, gboolean overwrite,
236  crm_time_t *next_change)
237 {
238  GList *pairs = make_pairs(xml_obj, set_name);
239 
240  if (pairs) {
242  .values = hash,
243  .first_id = always_first,
244  .overwrite = overwrite,
245  .next_change = next_change,
246  };
247 
248  map_rule_input(&(data.rule_input), rule_data);
249 
250  pairs = g_list_sort_with_data(pairs, sort_pairs, &data);
251  g_list_foreach(pairs, unpack_attr_set, &data);
252  g_list_free(pairs);
253  }
254 }
255 
269 void
270 pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
271  GHashTable *node_hash, GHashTable *hash,
272  const char *always_first, gboolean overwrite,
273  crm_time_t *now, crm_time_t *next_change)
274 {
275  pe_rule_eval_data_t rule_data = {
276  .node_hash = node_hash,
277  .now = now,
278  .match_data = NULL,
279  .rsc_data = NULL,
280  .op_data = NULL
281  };
282 
283  pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash,
284  always_first, overwrite, next_change);
285 }
286 
287 // Deprecated functions kept only for backward API compatibility
288 // LCOV_EXCL_START
289 
291 
292 gboolean
293 pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
294  crm_time_t *next_change)
295 {
296  pcmk_rule_input_t rule_input = { NULL, };
297 
298  map_rule_input(&rule_input, rule_data);
299  return pcmk__evaluate_rules(ruleset, &rule_input,
300  next_change) == pcmk_rc_ok;
301 }
302 
303 gboolean
304 pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now,
305  crm_time_t *next_change)
306 {
307  pcmk_rule_input_t rule_input = {
308  .node_attrs = node_hash,
309  .now = now,
310  };
311 
312  return pcmk__evaluate_rules(ruleset, &rule_input, next_change);
313 }
314 
315 gboolean
316 pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
317  crm_time_t *now, crm_time_t *next_change,
318  pe_match_data_t *match_data)
319 {
320  pcmk_rule_input_t rule_input = {
321  .node_attrs = node_hash,
322  .now = now,
323  };
324 
325  if (match_data != NULL) {
326  rule_input.rsc_params = match_data->params;
327  rule_input.rsc_meta = match_data->meta;
328  if (match_data->re != NULL) {
329  rule_input.rsc_id = match_data->re->string;
330  rule_input.rsc_id_submatches = match_data->re->pmatch;
331  rule_input.rsc_id_nmatches = match_data->re->nregs;
332  }
333  }
334  return pcmk_evaluate_rule(rule, &rule_input, next_change) == pcmk_rc_ok;
335 }
336 
337 gboolean
338 test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
339 {
340  return pe_evaluate_rules(ruleset, node_hash, now, NULL);
341 }
342 
343 gboolean
344 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
345 {
346  return pe_test_rule(rule, node_hash, role, now, NULL, NULL);
347 }
348 
349 gboolean
350 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)
351 {
352  pe_match_data_t match_data = {
353  .re = re_match_data,
354  .params = NULL,
355  .meta = NULL,
356  };
357  return pe_test_rule(rule, node_hash, role, now, NULL, &match_data);
358 }
359 
360 gboolean
361 pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
362  crm_time_t *now, pe_match_data_t *match_data)
363 {
364  return pe_test_rule(rule, node_hash, role, now, NULL, match_data);
365 }
366 
367 gboolean
368 pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role,
369  crm_time_t *now, crm_time_t *next_change,
370  pe_match_data_t *match_data)
371 {
372  pcmk_rule_input_t rule_input = {
373  .now = now,
374  .node_attrs = node_hash,
375  };
376 
377  if (match_data != NULL) {
378  rule_input.rsc_params = match_data->params;
379  rule_input.rsc_meta = match_data->meta;
380  if (match_data->re != NULL) {
381  rule_input.rsc_id = match_data->re->string;
382  rule_input.rsc_id_submatches = match_data->re->pmatch;
383  rule_input.rsc_id_nmatches = match_data->re->nregs;
384  }
385  }
386  return pcmk__evaluate_condition(expr, &rule_input,
387  next_change) == pcmk_rc_ok;
388 }
389 
390 gboolean
391 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
392 {
393  return pe_test_expression(expr, node_hash, role, now, NULL, NULL);
394 }
395 
396 gboolean
397 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)
398 {
399  pe_match_data_t match_data = {
400  .re = re_match_data,
401  .params = NULL,
402  .meta = NULL,
403  };
404  return pe_test_expression(expr, node_hash, role, now, NULL, &match_data);
405 }
406 
407 gboolean
408 pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
409  enum rsc_role_e role, crm_time_t *now,
410  pe_match_data_t *match_data)
411 {
412  return pe_test_expression(expr, node_hash, role, now, NULL, match_data);
413 }
414 
415 gboolean
416 pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data,
417  crm_time_t *next_change)
418 {
419  pcmk_rule_input_t rule_input = { NULL, };
420 
421  map_rule_input(&rule_input, rule_data);
422  return pcmk_evaluate_rule(rule, &rule_input, next_change) == pcmk_rc_ok;
423 }
424 
425 gboolean
426 pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data,
427  crm_time_t *next_change)
428 {
429  pcmk_rule_input_t rule_input = { NULL, };
430 
431  map_rule_input(&rule_input, rule_data);
432  return pcmk__evaluate_condition(expr, &rule_input,
433  next_change) == pcmk_rc_ok;
434 }
435 
436 void
437 unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
438  GHashTable *node_hash, GHashTable *hash,
439  const char *always_first, gboolean overwrite,
440  crm_time_t *now)
441 {
442  pe_rule_eval_data_t rule_data = {
443  .node_hash = node_hash,
444  .now = now,
445  .match_data = NULL,
446  .rsc_data = NULL,
447  .op_data = NULL
448  };
449 
450  pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash, always_first,
451  overwrite, NULL);
452 }
453 
454 enum expression_type
455 find_expression_type(xmlNode *expr)
456 {
457  return pcmk__condition_type(expr);
458 }
459 
460 char *
461 pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
462 {
463  if (match_data == NULL) {
464  return NULL;
465  }
466  return pcmk__replace_submatches(string, match_data->string,
467  match_data->pmatch, match_data->nregs);
468 }
469 
470 // LCOV_EXCL_STOP
471 // End deprecated API
void pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, GHashTable *node_hash, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *now, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition: rules.c:270
A dumping ground.
#define PCMK_XE_NVPAIR
Definition: xml_names.h:141
int pcmk_evaluate_rule(xmlNode *rule, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Evaluate a single rule, including all its conditions.
Definition: rules.c:1385
const char * provider
Definition: common.h:36
GHashTable * rsc_meta
Definition: rules.h:93
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:316
#define PCMK_XA_NAME
Definition: xml_names.h:325
Deprecated Pacemaker rule API.
char data[0]
Definition: cpg.c:58
const crm_time_t * now
Current time for rule evaluation purposes.
Definition: rules.h:59
Data used to evaluate a rule (any NULL items are ignored)
Definition: rules.h:57
GHashTable * rsc_params
Definition: rules.h:86
enum expression_type pcmk__condition_type(const xmlNode *condition)
Definition: rules.c:37
const char * name
Definition: cib.c:26
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:350
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define pcmk__config_warn(fmt...)
pe_re_match_data_t * re
Definition: common.h:29
#define PCMK__XE_ATTRIBUTES
CRM_TRACE_INIT_DATA(pe_rules)
int rsc_id_nmatches
Number of entries in rsc_id_submatches.
Definition: rules.h:102
expression_type
Definition: rules.h:33
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:397
const char * first_id
guint interval
Definition: common.h:42
pe_match_data_t * match_data
Definition: common.h:49
pe_op_eval_data_t * op_data
Definition: common.h:51
gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:293
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:446
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:440
int char2score(const char *score)
Get the integer value of a score string.
Definition: scores.c:36
#define crm_trace(fmt, args...)
Definition: logging.h:404
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)
Definition: rules.c:368
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2152
const char * op_name
Definition: common.h:41
gboolean pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:416
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:344
Wrappers for and extensions to libxml2.
rsc_role_e
Definition: roles.h:34
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:361
GHashTable * values
GHashTable * meta
Definition: common.h:31
gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, crm_time_t *next_change)
Definition: rules.c:304
#define PCMK_XA_VALUE
Definition: xml_names.h:437
#define PCMK_XA_SCORE
Definition: xml_names.h:391
int pcmk__evaluate_condition(xmlNode *condition, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1333
pe_rsc_eval_data_t * rsc_data
Definition: common.h:50
const char * agent
Definition: common.h:37
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
GHashTable * params
Definition: common.h:30
gboolean pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition: rules.c:426
const char * standard
Definition: common.h:35
crm_time_t * now
Definition: common.h:48
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:408
crm_time_t * next_change
char * pcmk__replace_submatches(const char *string, const char *match, const regmatch_t submatches[], int nmatches)
Definition: rules.c:685
GHashTable * node_hash
Definition: common.h:46
char * pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
Definition: rules.c:461
const regmatch_t * rsc_id_submatches
Resource pattern submatches (as set by regexec()) for rsc_id.
Definition: rules.h:99
regmatch_t * pmatch
Definition: common.h:25
int pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1486
GHashTable * node_attrs
Operation interval that rule applies to.
Definition: rules.h:77
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:455
pcmk_rule_input_t rule_input
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:391
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:437
const char * rsc_id
Resource ID to compare against a location constraint&#39;s resource pattern.
Definition: rules.h:96
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition: rules.c:338
void pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name, const pe_rule_eval_data_t *rule_data, GHashTable *hash, const char *always_first, gboolean overwrite, crm_time_t *next_change)
Extract nvpair blocks contained by an XML element into a hash table.
Definition: rules.c:233
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:701
char * string
Definition: common.h:23