LCOV - code coverage report
Current view: top level - pengine - rules.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 111 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 7 0.0 %

          Line data    Source code
       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           0 : map_rule_input(pcmk_rule_input_t *new, const pe_rule_eval_data_t *old)
      39             : {
      40           0 :     if (old == NULL) {
      41           0 :         return;
      42             :     }
      43           0 :     new->now = old->now;
      44           0 :     new->node_attrs = old->node_hash;
      45           0 :     if (old->rsc_data != NULL) {
      46           0 :         new->rsc_standard = old->rsc_data->standard;
      47           0 :         new->rsc_provider = old->rsc_data->provider;
      48           0 :         new->rsc_agent = old->rsc_data->agent;
      49             :     }
      50           0 :     if (old->match_data != NULL) {
      51           0 :         new->rsc_params = old->match_data->params;
      52           0 :         new->rsc_meta = old->match_data->meta;
      53           0 :         if (old->match_data->re != NULL) {
      54           0 :             new->rsc_id = old->match_data->re->string;
      55           0 :             new->rsc_id_submatches = old->match_data->re->pmatch;
      56           0 :             new->rsc_id_nmatches = old->match_data->re->nregs;
      57             :         }
      58             :     }
      59           0 :     if (old->op_data != NULL) {
      60           0 :         new->op_name = old->op_data->op_name;
      61           0 :         new->op_interval_ms = old->op_data->interval;
      62             :     }
      63             : }
      64             : 
      65             : static gint
      66           0 : sort_pairs(gconstpointer a, gconstpointer b, gpointer user_data)
      67             : {
      68           0 :     const xmlNode *pair_a = a;
      69           0 :     const xmlNode *pair_b = b;
      70           0 :     pcmk__nvpair_unpack_t *unpack_data = user_data;
      71             : 
      72           0 :     const char *score = NULL;
      73           0 :     int score_a = 0;
      74           0 :     int score_b = 0;
      75             : 
      76           0 :     if (a == NULL && b == NULL) {
      77           0 :         return 0;
      78           0 :     } else if (a == NULL) {
      79           0 :         return 1;
      80           0 :     } else if (b == NULL) {
      81           0 :         return -1;
      82             :     }
      83             : 
      84           0 :     if (pcmk__str_eq(pcmk__xe_id(pair_a), unpack_data->first_id,
      85             :                      pcmk__str_none)) {
      86           0 :         return -1;
      87             : 
      88           0 :     } else if (pcmk__str_eq(pcmk__xe_id(pair_b), unpack_data->first_id,
      89             :                             pcmk__str_none)) {
      90           0 :         return 1;
      91             :     }
      92             : 
      93           0 :     score = crm_element_value(pair_a, PCMK_XA_SCORE);
      94           0 :     score_a = char2score(score);
      95             : 
      96           0 :     score = crm_element_value(pair_b, PCMK_XA_SCORE);
      97           0 :     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           0 :     if (score_a < score_b) {
     104           0 :         return unpack_data->overwrite? -1 : 1;
     105           0 :     } else if (score_a > score_b) {
     106           0 :         return unpack_data->overwrite? 1 : -1;
     107             :     }
     108           0 :     return 0;
     109             : }
     110             : 
     111             : static void
     112           0 : populate_hash(xmlNode *nvpair_list, GHashTable *hash, bool overwrite)
     113             : {
     114           0 :     const char *name = NULL;
     115           0 :     const char *value = NULL;
     116           0 :     const char *old_value = NULL;
     117           0 :     xmlNode *list = nvpair_list;
     118           0 :     xmlNode *an_attr = NULL;
     119             : 
     120           0 :     if (pcmk__xe_is(list->children, PCMK__XE_ATTRIBUTES)) {
     121           0 :         list = list->children;
     122             :     }
     123             : 
     124           0 :     for (an_attr = pcmk__xe_first_child(list, NULL, NULL, NULL);
     125           0 :          an_attr != NULL; an_attr = pcmk__xe_next(an_attr)) {
     126             : 
     127           0 :         if (pcmk__xe_is(an_attr, PCMK_XE_NVPAIR)) {
     128           0 :             xmlNode *ref_nvpair = expand_idref(an_attr, NULL);
     129             : 
     130           0 :             name = crm_element_value(an_attr, PCMK_XA_NAME);
     131           0 :             if ((name == NULL) && (ref_nvpair != NULL)) {
     132           0 :                 name = crm_element_value(ref_nvpair, PCMK_XA_NAME);
     133             :             }
     134             : 
     135           0 :             value = crm_element_value(an_attr, PCMK_XA_VALUE);
     136           0 :             if ((value == NULL) && (ref_nvpair != NULL)) {
     137           0 :                 value = crm_element_value(ref_nvpair, PCMK_XA_VALUE);
     138             :             }
     139             : 
     140           0 :             if (name == NULL || value == NULL) {
     141           0 :                 continue;
     142             :             }
     143             : 
     144           0 :             old_value = g_hash_table_lookup(hash, name);
     145             : 
     146           0 :             if (pcmk__str_eq(value, "#default", pcmk__str_casei)) {
     147             :                 // @COMPAT Deprecated since 2.1.8
     148           0 :                 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           0 :                 if (old_value) {
     153           0 :                     crm_trace("Letting %s default (removing explicit value \"%s\")",
     154             :                               name, value);
     155           0 :                     g_hash_table_remove(hash, name);
     156             :                 }
     157           0 :                 continue;
     158             : 
     159           0 :             } else if (old_value == NULL) {
     160           0 :                 crm_trace("Setting %s=\"%s\"", name, value);
     161           0 :                 pcmk__insert_dup(hash, name, value);
     162             : 
     163           0 :             } else if (overwrite) {
     164           0 :                 crm_trace("Setting %s=\"%s\" (overwriting old value \"%s\")",
     165             :                           name, value, old_value);
     166           0 :                 pcmk__insert_dup(hash, name, value);
     167             :             }
     168             :         }
     169             :     }
     170           0 : }
     171             : 
     172             : static void
     173           0 : unpack_attr_set(gpointer data, gpointer user_data)
     174             : {
     175           0 :     xmlNode *pair = data;
     176           0 :     pcmk__nvpair_unpack_t *unpack_data = user_data;
     177             : 
     178           0 :     if (pcmk__evaluate_rules(pair, &(unpack_data->rule_input),
     179             :                              unpack_data->next_change) != pcmk_rc_ok) {
     180           0 :         return;
     181             :     }
     182             : 
     183           0 :     crm_trace("Adding name/value pairs from %s %s overwrite",
     184             :               pcmk__xe_id(pair), (unpack_data->overwrite? "with" : "without"));
     185           0 :     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           0 : make_pairs(const xmlNode *xml_obj, const char *set_name)
     199             : {
     200           0 :     GList *unsorted = NULL;
     201             : 
     202           0 :     if (xml_obj == NULL) {
     203           0 :         return NULL;
     204             :     }
     205           0 :     for (xmlNode *attr_set = pcmk__xe_first_child(xml_obj, NULL, NULL, NULL);
     206           0 :          attr_set != NULL; attr_set = pcmk__xe_next(attr_set)) {
     207             : 
     208           0 :         if ((set_name == NULL) || pcmk__xe_is(attr_set, set_name)) {
     209           0 :             xmlNode *expanded_attr_set = expand_idref(attr_set, NULL);
     210             : 
     211           0 :             if (expanded_attr_set == NULL) {
     212           0 :                 continue; // Not possible with schema validation enabled
     213             :             }
     214           0 :             unsorted = g_list_prepend(unsorted, expanded_attr_set);
     215             :         }
     216             :     }
     217           0 :     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           0 : 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           0 :     GList *pairs = make_pairs(xml_obj, set_name);
     239             : 
     240           0 :     if (pairs) {
     241           0 :         pcmk__nvpair_unpack_t data = {
     242             :             .values = hash,
     243             :             .first_id = always_first,
     244             :             .overwrite = overwrite,
     245             :             .next_change = next_change,
     246             :         };
     247             : 
     248           0 :         map_rule_input(&(data.rule_input), rule_data);
     249             : 
     250           0 :         pairs = g_list_sort_with_data(pairs, sort_pairs, &data);
     251           0 :         g_list_foreach(pairs, unpack_attr_set, &data);
     252           0 :         g_list_free(pairs);
     253             :     }
     254           0 : }
     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           0 : 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           0 :     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           0 :     pe_eval_nvpairs(NULL, xml_obj, set_name, &rule_data, hash,
     284             :                     always_first, overwrite, next_change);
     285           0 : }
     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,
     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

Generated by: LCOV version 1.14