pacemaker  2.1.9-49aab99839
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  int score_a = 0;
73  int score_b = 0;
74  int rc = pcmk_rc_ok;
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  rc = pcmk__xe_get_score(pair_a, PCMK_XA_SCORE, &score_a, 0);
94  if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
95  pcmk__config_warn("Using 0 as %s score because '%s' "
96  "is not a valid score: %s",
97  pcmk__xe_id(pair_a),
99  pcmk_rc_str(rc));
100  }
101 
102  rc = pcmk__xe_get_score(pair_b, PCMK_XA_SCORE, &score_b, 0);
103  if (rc != pcmk_rc_ok) { // Not possible with schema validation enabled
104  pcmk__config_warn("Using 0 as %s score because '%s' "
105  "is not a valid score: %s",
106  pcmk__xe_id(pair_b),
108  pcmk_rc_str(rc));
109  }
110 
111  /* If we're overwriting values, we want lowest score first, so the highest
112  * score is processed last; if we're not overwriting values, we want highest
113  * score first, so nothing else overwrites it.
114  */
115  if (score_a < score_b) {
116  return unpack_data->overwrite? -1 : 1;
117  } else if (score_a > score_b) {
118  return unpack_data->overwrite? 1 : -1;
119  }
120  return 0;
121 }
122 
123 static void
124 populate_hash(xmlNode *nvpair_list, GHashTable *hash, bool overwrite)
125 {
126  const char *name = NULL;
127  const char *value = NULL;
128  const char *old_value = NULL;
129  xmlNode *list = nvpair_list;
130  xmlNode *an_attr = NULL;
131 
132  if (pcmk__xe_is(list->children, PCMK__XE_ATTRIBUTES)) {
133  list = list->children;
134  }
135 
136  for (an_attr = pcmk__xe_first_child(list, NULL, NULL, NULL);
137  an_attr != NULL; an_attr = pcmk__xe_next(an_attr)) {
138 
139  if (pcmk__xe_is(an_attr, PCMK_XE_NVPAIR)) {
140  xmlNode *ref_nvpair = expand_idref(an_attr, NULL);
141 
142  name = crm_element_value(an_attr, PCMK_XA_NAME);
143  if ((name == NULL) && (ref_nvpair != NULL)) {
144  name = crm_element_value(ref_nvpair, PCMK_XA_NAME);
145  }
146 
147  value = crm_element_value(an_attr, PCMK_XA_VALUE);
148  if ((value == NULL) && (ref_nvpair != NULL)) {
149  value = crm_element_value(ref_nvpair, PCMK_XA_VALUE);
150  }
151 
152  if (name == NULL || value == NULL) {
153  continue;
154  }
155 
156  old_value = g_hash_table_lookup(hash, name);
157 
158  if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
159  // @COMPAT Deprecated since 2.1.8
160  pcmk__config_warn("Support for setting meta-attributes (such "
161  "as %s) to the explicit value '#default' is "
162  "deprecated and will be removed in a future "
163  "release", name);
164  if (old_value) {
165  crm_trace("Letting %s default (removing explicit value \"%s\")",
166  name, value);
167  g_hash_table_remove(hash, name);
168  }
169  continue;
170 
171  } else if (old_value == NULL) {
172  crm_trace("Setting %s=\"%s\"", name, value);
173  pcmk__insert_dup(hash, name, value);
174 
175  } else if (overwrite) {
176  crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")",
177  name, value, old_value);
178  pcmk__insert_dup(hash, name, value);
179  }
180  }
181  }
182 }
183 
184 static void
185 unpack_attr_set(gpointer data, gpointer user_data)
186 {
187  xmlNode *pair = data;
188  pcmk__nvpair_unpack_t *unpack_data = user_data;
189 
190  if (pcmk__evaluate_rules(pair, &(unpack_data->rule_input),
191  unpack_data->next_change) != pcmk_rc_ok) {
192  return;
193  }
194 
195  crm_trace("Adding name/value pairs from %s %s overwrite",
196  pcmk__xe_id(pair), (unpack_data->overwrite? "with" : "without"));
197  populate_hash(pair, unpack_data->values, unpack_data->overwrite);
198 }
199 
209 static GList *
210 make_pairs(const xmlNode *xml_obj, const char *set_name)
211 {
212  GList *unsorted = NULL;
213 
214  if (xml_obj == NULL) {
215  return NULL;
216  }
217  for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
218  attr_set != NULL; attr_set = pcmk__xe_next(attr_set)) {
219 
220  if ((set_name == NULL) || pcmk__xe_is(attr_set, set_name)) {
221  xmlNode *expanded_attr_set = expand_idref(attr_set, NULL);
222 
223  if (expanded_attr_set == NULL) {
224  continue; // Not possible with schema validation enabled
225  }
226  unsorted = g_list_prepend(unsorted, expanded_attr_set);
227  }
228  }
229  return unsorted;
230 }
231 
244 void
245 pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
246  const pe_rule_eval_data_t *rule_data, GHashTable *hash,
247  const char *always_first, gboolean overwrite,
248  crm_time_t *next_change)
249 {
250  GList *pairs = make_pairs(xml_obj, set_name);
251 
252  if (pairs) {
254  .values = hash,
255  .first_id = always_first,
256  .overwrite = overwrite,
257  .next_change = next_change,
258  };
259 
260  map_rule_input(&(data.rule_input), rule_data);
261 
262  pairs = g_list_sort_with_data(pairs, sort_pairs, &data);
263  g_list_foreach(pairs, unpack_attr_set, &data);
264  g_list_free(pairs);
265  }
266 }
267 
281 void
282 pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
283  GHashTable *node_hash, GHashTable *hash,
284  const char *always_first, gboolean overwrite,
285  crm_time_t *now, crm_time_t *next_change)
286 {
287  pe_rule_eval_data_t rule_data = {
288  .node_hash = node_hash,
289  .now = now,
290  .match_data = NULL,
291  .rsc_data = NULL,
292  .op_data = NULL
293  };
294 
295  pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash,
296  always_first, overwrite, next_change);
297 }
298 
299 // Deprecated functions kept only for backward API compatibility
300 // LCOV_EXCL_START
301 
303 
304 gboolean
305 pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
306  crm_time_t *next_change)
307 {
308  pcmk_rule_input_t rule_input = { NULL, };
309 
310  map_rule_input(&rule_input, rule_data);
311  return pcmk__evaluate_rules(ruleset, &rule_input,
312  next_change) == pcmk_rc_ok;
313 }
314 
315 gboolean
316 pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now,
317  crm_time_t *next_change)
318 {
319  pcmk_rule_input_t rule_input = {
320  .node_attrs = node_hash,
321  .now = now,
322  };
323 
324  return pcmk__evaluate_rules(ruleset, &rule_input, next_change);
325 }
326 
327 gboolean
328 pe_test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
329  crm_time_t *now, crm_time_t *next_change,
330  pe_match_data_t *match_data)
331 {
332  pcmk_rule_input_t rule_input = {
333  .node_attrs = node_hash,
334  .now = now,
335  };
336 
337  if (match_data != NULL) {
338  rule_input.rsc_params = match_data->params;
339  rule_input.rsc_meta = match_data->meta;
340  if (match_data->re != NULL) {
341  rule_input.rsc_id = match_data->re->string;
342  rule_input.rsc_id_submatches = match_data->re->pmatch;
343  rule_input.rsc_id_nmatches = match_data->re->nregs;
344  }
345  }
346  return pcmk_evaluate_rule(rule, &rule_input, next_change) == pcmk_rc_ok;
347 }
348 
349 gboolean
350 test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
351 {
352  return pe_evaluate_rules(ruleset, node_hash, now, NULL);
353 }
354 
355 gboolean
356 test_rule(xmlNode * rule, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
357 {
358  return pe_test_rule(rule, node_hash, role, now, NULL, NULL);
359 }
360 
361 gboolean
362 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)
363 {
364  pe_match_data_t match_data = {
365  .re = re_match_data,
366  .params = NULL,
367  .meta = NULL,
368  };
369  return pe_test_rule(rule, node_hash, role, now, NULL, &match_data);
370 }
371 
372 gboolean
373 pe_test_rule_full(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role,
374  crm_time_t *now, pe_match_data_t *match_data)
375 {
376  return pe_test_rule(rule, node_hash, role, now, NULL, match_data);
377 }
378 
379 gboolean
380 pe_test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role,
381  crm_time_t *now, crm_time_t *next_change,
382  pe_match_data_t *match_data)
383 {
384  pcmk_rule_input_t rule_input = {
385  .now = now,
386  .node_attrs = node_hash,
387  };
388 
389  if (match_data != NULL) {
390  rule_input.rsc_params = match_data->params;
391  rule_input.rsc_meta = match_data->meta;
392  if (match_data->re != NULL) {
393  rule_input.rsc_id = match_data->re->string;
394  rule_input.rsc_id_submatches = match_data->re->pmatch;
395  rule_input.rsc_id_nmatches = match_data->re->nregs;
396  }
397  }
398  return pcmk__evaluate_condition(expr, &rule_input,
399  next_change) == pcmk_rc_ok;
400 }
401 
402 gboolean
403 test_expression(xmlNode * expr, GHashTable * node_hash, enum rsc_role_e role, crm_time_t * now)
404 {
405  return pe_test_expression(expr, node_hash, role, now, NULL, NULL);
406 }
407 
408 gboolean
409 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)
410 {
411  pe_match_data_t match_data = {
412  .re = re_match_data,
413  .params = NULL,
414  .meta = NULL,
415  };
416  return pe_test_expression(expr, node_hash, role, now, NULL, &match_data);
417 }
418 
419 gboolean
420 pe_test_expression_full(xmlNode *expr, GHashTable *node_hash,
421  enum rsc_role_e role, crm_time_t *now,
422  pe_match_data_t *match_data)
423 {
424  return pe_test_expression(expr, node_hash, role, now, NULL, match_data);
425 }
426 
427 gboolean
428 pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data,
429  crm_time_t *next_change)
430 {
431  pcmk_rule_input_t rule_input = { NULL, };
432 
433  map_rule_input(&rule_input, rule_data);
434  return pcmk_evaluate_rule(rule, &rule_input, next_change) == pcmk_rc_ok;
435 }
436 
437 gboolean
438 pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data,
439  crm_time_t *next_change)
440 {
441  pcmk_rule_input_t rule_input = { NULL, };
442 
443  map_rule_input(&rule_input, rule_data);
444  return pcmk__evaluate_condition(expr, &rule_input,
445  next_change) == pcmk_rc_ok;
446 }
447 
448 void
449 unpack_instance_attributes(xmlNode *top, xmlNode *xml_obj, const char *set_name,
450  GHashTable *node_hash, GHashTable *hash,
451  const char *always_first, gboolean overwrite,
452  crm_time_t *now)
453 {
454  pe_rule_eval_data_t rule_data = {
455  .node_hash = node_hash,
456  .now = now,
457  .match_data = NULL,
458  .rsc_data = NULL,
459  .op_data = NULL
460  };
461 
462  pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash, always_first,
463  overwrite, NULL);
464 }
465 
466 enum expression_type
467 find_expression_type(xmlNode *expr)
468 {
469  return pcmk__condition_type(expr);
470 }
471 
472 char *
473 pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
474 {
475  if (match_data == NULL) {
476  return NULL;
477  }
478  return pcmk__replace_submatches(string, match_data->string,
479  match_data->pmatch, match_data->nregs);
480 }
481 
482 // LCOV_EXCL_STOP
483 // 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:282
A dumping ground.
#define PCMK_XE_NVPAIR
Definition: xml_names.h:144
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:1386
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:328
#define PCMK_XA_NAME
Definition: xml_names.h:330
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:362
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
const char * pcmk_rc_str(int rc)
Get a user-friendly description of a return code.
Definition: results.c:503
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:409
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:305
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:458
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml.c:481
#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:380
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition: xml.c:2170
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:428
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition: rules.c:356
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:373
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:316
#define PCMK_XA_VALUE
Definition: xml_names.h:442
#define PCMK_XA_SCORE
Definition: xml_names.h:396
int pcmk__evaluate_condition(xmlNode *condition, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1334
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:438
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:420
crm_time_t * next_change
char * pcmk__replace_submatches(const char *string, const char *match, const regmatch_t submatches[], int nmatches)
Definition: rules.c:686
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:473
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__xe_get_score(const xmlNode *xml, const char *name, int *score, int default_score)
Definition: xml.c:549
int pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition: rules.c:1487
GHashTable * node_attrs
Operation interval that rule applies to.
Definition: rules.h:77
enum expression_type find_expression_type(xmlNode *expr)
Definition: rules.c:467
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:403
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:449
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:350
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:245
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition: strings.c:713
char * string
Definition: common.h:23