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     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),
  98                           crm_element_value(pair_a, PCMK_XA_SCORE),
  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),
 107                           crm_element_value(pair_b, PCMK_XA_SCORE),
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 200 /*!
 201  * \internal
 202  * \brief Create a sorted list of nvpair blocks
 203  *
 204  * \param[in]     xml_obj       XML element containing blocks of nvpair elements
 205  * \param[in]     set_name      If not NULL, only get blocks of this element
 206  *
 207  * \return List of XML blocks of name/value pairs
 208  */
 209 static GList *
 210 make_pairs(const xmlNode *xml_obj, const char *set_name)
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 232 /*!
 233  * \brief Extract nvpair blocks contained by an XML element into a hash table
 234  *
 235  * \param[in,out] top           Ignored
 236  * \param[in]     xml_obj       XML element containing blocks of nvpair elements
 237  * \param[in]     set_name      If not NULL, only use blocks of this element
 238  * \param[in]     rule_data     Matching parameters to use when unpacking
 239  * \param[out]    hash          Where to store extracted name/value pairs
 240  * \param[in]     always_first  If not NULL, process block with this ID first
 241  * \param[in]     overwrite     Whether to replace existing values with same name
 242  * \param[out]    next_change   If not NULL, set to when evaluation will change
 243  */
 244 void
 245 pe_eval_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 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) {
 253         pcmk__nvpair_unpack_t data = {
 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 
 268 /*!
 269  * \brief Extract nvpair blocks contained by an XML element into a hash table
 270  *
 271  * \param[in,out] top           Ignored
 272  * \param[in]     xml_obj       XML element containing blocks of nvpair elements
 273  * \param[in]     set_name      Element name to identify nvpair blocks
 274  * \param[in]     node_hash     Node attributes to use when evaluating rules
 275  * \param[out]    hash          Where to store extracted name/value pairs
 276  * \param[in]     always_first  If not NULL, process block with this ID first
 277  * \param[in]     overwrite     Whether to replace existing values with same name
 278  * \param[in]     now           Time to use when evaluating rules
 279  * \param[out]    next_change   If not NULL, set to when evaluation will change
 280  */
 281 void
 282 pe_unpack_nvpairs(xmlNode *top, const xmlNode *xml_obj, const char *set_name,
     /* [previous][next][first][last][top][bottom][index][help] */
 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 
 302 #include <crm/pengine/rules_compat.h>
 303 
 304 gboolean
 305 pe_eval_rules(xmlNode *ruleset, const pe_rule_eval_data_t *rule_data,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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,
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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)
     /* [previous][next][first][last][top][bottom][index][help] */
 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

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