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

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