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

          Line data    Source code
       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           0 : eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error)
      36             : {
      37           0 :     xmlNodePtr cib_constraints = NULL;
      38           0 :     xmlNodePtr match = NULL;
      39           0 :     xmlXPathObjectPtr xpath_obj = NULL;
      40           0 :     char *xpath = NULL;
      41           0 :     int rc = pcmk_rc_ok;
      42           0 :     int num_results = 0;
      43             : 
      44           0 :     *error = NULL;
      45             : 
      46             :     /* Rules are under the constraints node in the XML, so first find that. */
      47           0 :     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= but does not contain
      55             :      *   moon=.
      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           0 :     xpath = crm_strdup_printf(XPATH_NODE_RULE, rule_id);
      61           0 :     xpath_obj = xpath_search(cib_constraints, xpath);
      62           0 :     num_results = numXpathResults(xpath_obj);
      63             : 
      64           0 :     free(xpath);
      65           0 :     freeXpathObject(xpath_obj);
      66             : 
      67           0 :     if (num_results == 0) {
      68           0 :         *error = "Rule not found";
      69           0 :         return ENXIO;
      70             :     }
      71             : 
      72           0 :     if (num_results > 1) {
      73             :         // Should not be possible; schema prevents this
      74           0 :         *error = "Found more than one rule with matching ID";
      75           0 :         return pcmk_rc_duplicate_id;
      76             :     }
      77             : 
      78             :     /* Next, make sure it has exactly one date_expression. */
      79           0 :     xpath = crm_strdup_printf(XPATH_NODE_RULE "//date_expression", rule_id);
      80           0 :     xpath_obj = xpath_search(cib_constraints, xpath);
      81           0 :     num_results = numXpathResults(xpath_obj);
      82             : 
      83           0 :     free(xpath);
      84           0 :     freeXpathObject(xpath_obj);
      85             : 
      86           0 :     if (num_results != 1) {
      87           0 :         if (num_results == 0) {
      88           0 :             *error = "Rule does not have a date expression";
      89             :         } else {
      90           0 :             *error = "Rule has more than one date expression";
      91             :         }
      92           0 :         return EOPNOTSUPP;
      93             :     }
      94             : 
      95             :     /* Then, check that it's something we actually support. */
      96           0 :     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           0 :     xpath_obj = xpath_search(cib_constraints, xpath);
     102           0 :     num_results = numXpathResults(xpath_obj);
     103             : 
     104           0 :     free(xpath);
     105             : 
     106           0 :     if (num_results == 0) {
     107           0 :         freeXpathObject(xpath_obj);
     108             : 
     109           0 :         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             :                                   "and not(" PCMK_XE_DATE_SPEC
     116             :                                       "/@" PCMK__XA_MOON ")]",
     117             :                                   rule_id);
     118           0 :         xpath_obj = xpath_search(cib_constraints, xpath);
     119           0 :         num_results = numXpathResults(xpath_obj);
     120             : 
     121           0 :         free(xpath);
     122             : 
     123           0 :         if (num_results == 0) {
     124           0 :             freeXpathObject(xpath_obj);
     125           0 :             *error = "Rule must either not use " PCMK_XE_DATE_SPEC ", or use "
     126             :                      PCMK_XE_DATE_SPEC " with " PCMK_XA_YEARS "= but not "
     127             :                      PCMK__XA_MOON "=";
     128           0 :             return EOPNOTSUPP;
     129             :         }
     130             :     }
     131             : 
     132           0 :     match = getXpathResult(xpath_obj, 0);
     133             : 
     134             :     /* We should have ensured this with the xpath query above, but double-
     135             :      * checking can't hurt.
     136             :      */
     137           0 :     CRM_ASSERT(match != NULL);
     138           0 :     CRM_ASSERT(pcmk__condition_type(match) == pcmk__condition_datetime);
     139             : 
     140           0 :     rc = pcmk__evaluate_date_expression(match, scheduler->now, NULL);
     141           0 :     if (rc == pcmk_rc_undetermined) { // Malformed or missing
     142           0 :         *error = "Error parsing rule";
     143             :     }
     144             : 
     145           0 :     freeXpathObject(xpath_obj);
     146           0 :     return rc;
     147             : }
     148             : 
     149             : /*!
     150             :  * \internal
     151             :  * \brief Check whether each rule in a list is in effect
     152             :  *
     153             :  * \param[in,out] out       Output object
     154             :  * \param[in]     input     The CIB XML to check (if \c NULL, use current CIB)
     155             :  * \param[in]     date      Check whether the rule is in effect at this date and
     156             :  *                          time (if \c NULL, use current date and time)
     157             :  * \param[in]     rule_ids  The IDs of the rules to check, as a <tt>NULL</tt>-
     158             :  *                          terminated list.
     159             :  *
     160             :  * \return Standard Pacemaker return code
     161             :  */
     162             : int
     163           0 : pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date,
     164             :                   const char **rule_ids)
     165             : {
     166           0 :     pcmk_scheduler_t *scheduler = NULL;
     167           0 :     int rc = pcmk_rc_ok;
     168             : 
     169           0 :     CRM_ASSERT(out != NULL);
     170             : 
     171           0 :     if (rule_ids == NULL) {
     172             :         // Trivial case; every rule specified is in effect
     173           0 :         return pcmk_rc_ok;
     174             :     }
     175             : 
     176           0 :     rc = pcmk__init_scheduler(out, input, date, &scheduler);
     177           0 :     if (rc != pcmk_rc_ok) {
     178           0 :         return rc;
     179             :     }
     180             : 
     181           0 :     for (const char **rule_id = rule_ids; *rule_id != NULL; rule_id++) {
     182           0 :         const char *error = NULL;
     183           0 :         int last_rc = eval_rule(scheduler, *rule_id, &error);
     184             : 
     185           0 :         out->message(out, "rule-check", *rule_id, last_rc, error);
     186             : 
     187           0 :         if (last_rc != pcmk_rc_ok) {
     188           0 :             rc = last_rc;
     189             :         }
     190             :     }
     191             : 
     192           0 :     pe_free_working_set(scheduler);
     193           0 :     return rc;
     194             : }
     195             : 
     196             : // Documented in pacemaker.h
     197             : int
     198           0 : pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date,
     199             :                  const char **rule_ids)
     200             : {
     201           0 :     pcmk__output_t *out = NULL;
     202           0 :     int rc = pcmk_rc_ok;
     203             : 
     204           0 :     rc = pcmk__xml_output_new(&out, xml);
     205           0 :     if (rc != pcmk_rc_ok) {
     206           0 :         return rc;
     207             :     }
     208             : 
     209           0 :     pcmk__register_lib_messages(out);
     210             : 
     211           0 :     rc = pcmk__check_rules(out, input, date, rule_ids);
     212           0 :     pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
     213           0 :     return rc;
     214             : }

Generated by: LCOV version 1.14