root/lib/pengine/rules.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. map_rule_input
  2. sort_pairs
  3. populate_hash
  4. unpack_attr_set
  5. make_pairs
  6. pe_eval_nvpairs
  7. pe_unpack_nvpairs
  8. pe_eval_rules
  9. pe_evaluate_rules
  10. pe_test_rule
  11. test_ruleset
  12. test_rule
  13. pe_test_rule_re
  14. pe_test_rule_full
  15. pe_test_expression
  16. test_expression
  17. pe_test_expression_re
  18. pe_test_expression_full
  19. pe_eval_expr
  20. pe_eval_subexpr
  21. unpack_instance_attributes
  22. find_expression_type
  23. pe_expand_re_matches

   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 
  18 #include <crm/common/iso8601_internal.h>
  19 #include <crm/common/nvpair_internal.h>
  20 #include <crm/common/rules_internal.h>
  21 #include <crm/common/xml_internal.h>
  22 #include <crm/pengine/internal.h>
  23 #include <crm/pengine/rules_internal.h>
  24 
  25 #include <sys/types.h>
  26 #include <regex.h>
  27 
  28 CRM_TRACE_INIT_DATA(pe_rules);
  29 
  30 /*!
  31  * \internal
  32  * \brief Map pe_rule_eval_data_t to pcmk_rule_input_t
  33  *
  34  * \param[out] new  New data struct
  35  * \param[in]  old  Old data struct
  36  */
  37 static void
  38 map_rule_input(pcmk_rule_input_t *new, const pe_rule_eval_data_t *old)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
  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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 188 /*!
 189  * \internal
 190  * \brief Create a sorted list of nvpair blocks
 191  *
 192  * \param[in]     xml_obj       XML element containing blocks of nvpair elements
 193  * \param[in]     set_name      If not NULL, only get blocks of this element
 194  *
 195  * \return List of XML blocks of name/value pairs
 196  */
 197 static GList *
 198 make_pairs(const xmlNode *xml_obj, const char *set_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 220 /*!
 221  * \brief Extract nvpair blocks contained by an XML element into a hash table
 222  *
 223  * \param[in,out] top           Ignored
 224  * \param[in]     xml_obj       XML element containing blocks of nvpair elements
 225  * \param[in]     set_name      If not NULL, only use blocks of this element
 226  * \param[in]     rule_data     Matching parameters to use when unpacking
 227  * \param[out]    hash          Where to store extracted name/value pairs
 228  * \param[in]     always_first  If not NULL, process block with this ID first
 229  * \param[in]     overwrite     Whether to replace existing values with same name
 230  * \param[out]    next_change   If not NULL, set to when evaluation will change
 231  */
 232 void
 233 pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
 241         pcmk__nvpair_unpack_t data = {
 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 
 256 /*!
 257  * \brief Extract nvpair blocks contained by an XML element into a hash table
 258  *
 259  * \param[in,out] top           Ignored
 260  * \param[in]     xml_obj       XML element containing blocks of nvpair elements
 261  * \param[in]     set_name      Element name to identify nvpair blocks
 262  * \param[in]     node_hash     Node attributes to use when evaluating rules
 263  * \param[out]    hash          Where to store extracted name/value pairs
 264  * \param[in]     always_first  If not NULL, process block with this ID first
 265  * \param[in]     overwrite     Whether to replace existing values with same name
 266  * \param[in]     now           Time to use when evaluating rules
 267  * \param[out]    next_change   If not NULL, set to when evaluation will change
 268  */
 269 void
 270 pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 290 #include <crm/pengine/rules_compat.h>
 291 
 292 gboolean
 293 pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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

/* [previous][next][first][last][top][bottom][index][help] */