root/lib/pacemaker/pcmk_rule.c

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

DEFINITIONS

This source file includes following definitions.
  1. eval_rule
  2. pcmk__check_rules
  3. pcmk_check_rules

   1 /*
   2  * Copyright 2022-2025 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 <libxml/xpath.h>           // xmlXPathObject, etc.
  13 
  14 #include <crm/cib/internal.h>
  15 #include <crm/common/cib.h>
  16 #include <crm/common/iso8601.h>
  17 #include <crm/common/xml.h>
  18 #include <crm/pengine/internal.h>
  19 #include <pacemaker-internal.h>
  20 
  21 #include "libpacemaker_private.h"
  22 
  23 #define XPATH_NODE_RULE "//" PCMK_XE_RULE "[@" PCMK_XA_ID "='%s']"
  24 
  25 /*!
  26  * \internal
  27  * \brief Check whether a given rule is in effect
  28  *
  29  * \param[in]     scheduler  Scheduler data
  30  * \param[in]     rule_id    The ID of the rule to check
  31  * \param[out]    error      Where to store a rule evaluation error message
  32  *
  33  * \return Standard Pacemaker return code
  34  */
  35 static int
  36 eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     xmlNodePtr cib_constraints = NULL;
  39     xmlNodePtr match = NULL;
  40     xmlXPathObject *xpath_obj = NULL;
  41     char *xpath = NULL;
  42     int rc = pcmk_rc_ok;
  43     int num_results = 0;
  44 
  45     *error = NULL;
  46 
  47     /* Rules are under the constraints node in the XML, so first find that. */
  48     cib_constraints = pcmk_find_cib_element(scheduler->input,
  49                                             PCMK_XE_CONSTRAINTS);
  50 
  51     /* Get all rules matching the given ID that are also simple enough for us
  52      * to check. For the moment, these rules must only have a single
  53      * date_expression child and:
  54      * - Do not have a date_spec operation, or
  55      * - Have a date_spec operation that contains years=
  56      *
  57      * We do this in steps to provide better error messages. First, check that
  58      * there's any rule with the given ID.
  59      */
  60     xpath = crm_strdup_printf(XPATH_NODE_RULE, rule_id);
  61     xpath_obj = pcmk__xpath_search(cib_constraints->doc, xpath);
  62     num_results = pcmk__xpath_num_results(xpath_obj);
  63 
  64     free(xpath);
  65     xmlXPathFreeObject(xpath_obj);
  66 
  67     if (num_results == 0) {
  68         *error = "Rule not found";
  69         return ENXIO;
  70     }
  71 
  72     if (num_results > 1) {
  73         // Should not be possible; schema prevents this
  74         *error = "Found more than one rule with matching ID";
  75         return pcmk_rc_duplicate_id;
  76     }
  77 
  78     /* Next, make sure it has exactly one date_expression. */
  79     xpath = crm_strdup_printf(XPATH_NODE_RULE "//date_expression", rule_id);
  80     xpath_obj = pcmk__xpath_search(cib_constraints->doc, xpath);
  81     num_results = pcmk__xpath_num_results(xpath_obj);
  82 
  83     free(xpath);
  84     xmlXPathFreeObject(xpath_obj);
  85 
  86     if (num_results != 1) {
  87         if (num_results == 0) {
  88             *error = "Rule does not have a date expression";
  89         } else {
  90             *error = "Rule has more than one date expression";
  91         }
  92         return EOPNOTSUPP;
  93     }
  94 
  95     /* Then, check that it's something we actually support. */
  96     xpath = crm_strdup_printf(XPATH_NODE_RULE
  97                               "//" PCMK_XE_DATE_EXPRESSION
  98                               "[@" PCMK_XA_OPERATION
  99                                   "!='" PCMK_VALUE_DATE_SPEC "']",
 100                               rule_id);
 101     xpath_obj = pcmk__xpath_search(cib_constraints->doc, xpath);
 102     num_results = pcmk__xpath_num_results(xpath_obj);
 103 
 104     free(xpath);
 105 
 106     if (num_results == 0) {
 107         xmlXPathFreeObject(xpath_obj);
 108 
 109         xpath = crm_strdup_printf(XPATH_NODE_RULE
 110                                   "//" PCMK_XE_DATE_EXPRESSION
 111                                   "[@" PCMK_XA_OPERATION
 112                                       "='" PCMK_VALUE_DATE_SPEC "' "
 113                                   "and " PCMK_XE_DATE_SPEC
 114                                       "/@" PCMK_XA_YEARS "]",
 115                                   rule_id);
 116         xpath_obj = pcmk__xpath_search(cib_constraints->doc, xpath);
 117         num_results = pcmk__xpath_num_results(xpath_obj);
 118 
 119         free(xpath);
 120 
 121         if (num_results == 0) {
 122             xmlXPathFreeObject(xpath_obj);
 123             *error = "Rule must either not use " PCMK_XE_DATE_SPEC ", or use "
 124                      PCMK_XE_DATE_SPEC " with " PCMK_XA_YEARS "=";
 125             return EOPNOTSUPP;
 126         }
 127     }
 128 
 129     match = pcmk__xpath_result(xpath_obj, 0);
 130 
 131     /* We should have ensured this with the xpath query above, but double-
 132      * checking can't hurt.
 133      */
 134     pcmk__assert((match != NULL)
 135                  && (pcmk__condition_type(match) == pcmk__condition_datetime));
 136 
 137     rc = pcmk__evaluate_date_expression(match, scheduler->priv->now, NULL);
 138     if ((rc != pcmk_rc_ok) && (rc != pcmk_rc_within_range)) {
 139         // Malformed or missing
 140         *error = "Error parsing rule";
 141     }
 142 
 143     xmlXPathFreeObject(xpath_obj);
 144     return rc;
 145 }
 146 
 147 /*!
 148  * \internal
 149  * \brief Check whether each rule in a list is in effect
 150  *
 151  * \param[in,out] out       Output object
 152  * \param[in]     input     The CIB XML to check (if \c NULL, use current CIB)
 153  * \param[in]     date      Check whether the rule is in effect at this date and
 154  *                          time (if \c NULL, use current date and time)
 155  * \param[in]     rule_ids  The IDs of the rules to check, as a <tt>NULL</tt>-
 156  *                          terminated list.
 157  *
 158  * \return Standard Pacemaker return code
 159  */
 160 int
 161 pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date,
     /* [previous][next][first][last][top][bottom][index][help] */
 162                   const char **rule_ids)
 163 {
 164     pcmk_scheduler_t *scheduler = NULL;
 165     int rc = pcmk_rc_ok;
 166 
 167     pcmk__assert(out != NULL);
 168 
 169     if (rule_ids == NULL) {
 170         // Trivial case; every rule specified is in effect
 171         return pcmk_rc_ok;
 172     }
 173 
 174     rc = pcmk__init_scheduler(out, input, date, &scheduler);
 175     if (rc != pcmk_rc_ok) {
 176         return rc;
 177     }
 178 
 179     for (const char **rule_id = rule_ids; *rule_id != NULL; rule_id++) {
 180         const char *error = NULL;
 181         int last_rc = eval_rule(scheduler, *rule_id, &error);
 182 
 183         out->message(out, "rule-check", *rule_id, last_rc, error);
 184 
 185         if (last_rc != pcmk_rc_ok) {
 186             rc = last_rc;
 187         }
 188     }
 189 
 190     pcmk_free_scheduler(scheduler);
 191     return rc;
 192 }
 193 
 194 // Documented in pacemaker.h
 195 int
 196 pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date,
     /* [previous][next][first][last][top][bottom][index][help] */
 197                  const char **rule_ids)
 198 {
 199     pcmk__output_t *out = NULL;
 200     int rc = pcmk_rc_ok;
 201 
 202     rc = pcmk__xml_output_new(&out, xml);
 203     if (rc != pcmk_rc_ok) {
 204         return rc;
 205     }
 206 
 207     pcmk__register_lib_messages(out);
 208 
 209     rc = pcmk__check_rules(out, input, date, rule_ids);
 210     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
 211     return rc;
 212 }

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