pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
24
25#include <sys/types.h>
26#include <regex.h>
27
29
37static void
38map_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
65static gint
66sort_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,
86 return -1;
87
88 } else if (pcmk__str_eq(pcmk__xe_id(pair_b), unpack_data->first_id,
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
111static void
112populate_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
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
172static void
173unpack_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
197static GList *
198make_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
232void
233pe_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
269void
270pe_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
292gboolean
293pe_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
303gboolean
304pe_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
315gboolean
316pe_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
337gboolean
338test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
339{
340 return pe_evaluate_rules(ruleset, node_hash, now, NULL);
341}
342
343gboolean
344test_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
349gboolean
350pe_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
360gboolean
361pe_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
367gboolean
368pe_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
390gboolean
391test_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
396gboolean
397pe_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
407gboolean
408pe_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
415gboolean
416pe_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
425gboolean
426pe_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
436void
437unpack_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
456{
457 return pcmk__condition_type(expr);
458}
459
460char *
461pe_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
const char * name
Definition cib.c:26
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
int pcmk__evaluate_rules(xmlNode *xml, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition rules.c:1486
enum expression_type pcmk__condition_type(const xmlNode *condition)
Definition rules.c:37
char * pcmk__replace_submatches(const char *string, const char *match, const regmatch_t submatches[], int nmatches)
Definition rules.c:685
int pcmk__evaluate_condition(xmlNode *condition, const pcmk_rule_input_t *rule_input, crm_time_t *next_change)
Definition rules.c:1333
expression_type
Definition rules.h:33
void populate_hash(xmlNode *nvpair_list, GHashTable *hash, const char **attrs, int attrs_length)
char data[0]
Definition cpg.c:10
A dumping ground.
struct crm_time_s crm_time_t
Definition iso8601.h:32
#define CRM_TRACE_INIT_DATA(name)
Definition logging.h:143
#define crm_trace(fmt, args...)
Definition logging.h:402
#define pcmk__config_warn(fmt...)
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition nvpair.c:446
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
enum expression_type find_expression_type(xmlNode *expr)
Definition rules.c:455
gboolean test_rule(xmlNode *rule, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition rules.c:344
char * pe_expand_re_matches(const char *string, const pe_re_match_data_t *match_data)
Definition rules.c:461
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
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
gboolean pe_evaluate_rules(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now, crm_time_t *next_change)
Definition rules.c:304
gboolean test_expression(xmlNode *expr, GHashTable *node_hash, enum rsc_role_e role, crm_time_t *now)
Definition rules.c:391
gboolean pe_eval_subexpr(xmlNode *expr, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition rules.c:426
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
gboolean pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition rules.c:293
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
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
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
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
gboolean pe_eval_expr(xmlNode *rule, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition rules.c:416
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
gboolean test_ruleset(xmlNode *ruleset, GHashTable *node_hash, crm_time_t *now)
Definition rules.c:338
@ pcmk_rc_ok
Definition results.h:162
rsc_role_e
Definition roles.h:34
Deprecated Pacemaker rule API.
int char2score(const char *score)
Get the integer value of a score string.
Definition scores.c:36
void pcmk__insert_dup(GHashTable *table, const char *name, const char *value)
Definition strings.c:701
@ pcmk__str_none
@ pcmk__str_casei
Data used to evaluate a rule (any NULL items are ignored)
Definition rules.h:57
const regmatch_t * rsc_id_submatches
Resource pattern submatches (as set by regexec()) for rsc_id.
Definition rules.h:99
int rsc_id_nmatches
Number of entries in rsc_id_submatches.
Definition rules.h:102
GHashTable * node_attrs
Operation interval that rule applies to.
Definition rules.h:77
GHashTable * rsc_meta
Definition rules.h:93
const crm_time_t * now
Current time for rule evaluation purposes.
Definition rules.h:59
const char * rsc_id
Resource ID to compare against a location constraint's resource pattern.
Definition rules.h:96
GHashTable * rsc_params
Definition rules.h:86
GHashTable * meta
Definition common.h:31
pe_re_match_data_t * re
Definition common.h:29
GHashTable * params
Definition common.h:30
const char * op_name
Definition common.h:41
guint interval
Definition common.h:42
regmatch_t * pmatch
Definition common.h:25
char * string
Definition common.h:23
const char * agent
Definition common.h:37
const char * provider
Definition common.h:36
const char * standard
Definition common.h:35
pe_rsc_eval_data_t * rsc_data
Definition common.h:50
GHashTable * node_hash
Definition common.h:46
crm_time_t * now
Definition common.h:48
pe_match_data_t * match_data
Definition common.h:49
pe_op_eval_data_t * op_data
Definition common.h:51
pcmk_rule_input_t rule_input
const char * first_id
crm_time_t * next_change
GHashTable * values
Wrappers for and extensions to libxml2.
xmlNode * expand_idref(xmlNode *input, xmlNode *top)
Definition xml.c:2152
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml.c:440
#define PCMK_XA_SCORE
Definition xml_names.h:391
#define PCMK_XA_VALUE
Definition xml_names.h:437
#define PCMK_XE_NVPAIR
Definition xml_names.h:141
#define PCMK_XA_NAME
Definition xml_names.h:325
#define PCMK__XE_ATTRIBUTES