pacemaker  2.1.9-49aab99839
Scalable High-Availability cluster resource manager
pcmk_rule.c
Go to the documentation of this file.
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>
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 
34 static int
35 eval_rule(pcmk_scheduler_t *scheduler, const char *rule_id, const char **error)
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,
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  xpath = crm_strdup_printf(XPATH_NODE_RULE, rule_id);
61  xpath_obj = xpath_search(cib_constraints, xpath);
62  num_results = numXpathResults(xpath_obj);
63 
64  free(xpath);
65  freeXpathObject(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 = xpath_search(cib_constraints, xpath);
81  num_results = numXpathResults(xpath_obj);
82 
83  free(xpath);
84  freeXpathObject(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. */
99  "!='" PCMK_VALUE_DATE_SPEC "']",
100  rule_id);
101  xpath_obj = xpath_search(cib_constraints, xpath);
102  num_results = numXpathResults(xpath_obj);
103 
104  free(xpath);
105 
106  if (num_results == 0) {
107  freeXpathObject(xpath_obj);
108 
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  xpath_obj = xpath_search(cib_constraints, xpath);
119  num_results = numXpathResults(xpath_obj);
120 
121  free(xpath);
122 
123  if (num_results == 0) {
124  freeXpathObject(xpath_obj);
125  *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  return EOPNOTSUPP;
129  }
130  }
131 
132  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  pcmk__assert((match != NULL)
139 
140  rc = pcmk__evaluate_date_expression(match, scheduler->now, NULL);
141  if (rc == pcmk_rc_undetermined) { // Malformed or missing
142  *error = "Error parsing rule";
143  }
144 
145  freeXpathObject(xpath_obj);
146  return rc;
147 }
148 
162 int
163 pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date,
164  const char **rule_ids)
165 {
166  pcmk_scheduler_t *scheduler = NULL;
167  int rc = pcmk_rc_ok;
168 
169  pcmk__assert(out != NULL);
170 
171  if (rule_ids == NULL) {
172  // Trivial case; every rule specified is in effect
173  return pcmk_rc_ok;
174  }
175 
176  rc = pcmk__init_scheduler(out, input, date, &scheduler);
177  if (rc != pcmk_rc_ok) {
178  return rc;
179  }
180 
181  for (const char **rule_id = rule_ids; *rule_id != NULL; rule_id++) {
182  const char *error = NULL;
183  int last_rc = eval_rule(scheduler, *rule_id, &error);
184 
185  out->message(out, "rule-check", *rule_id, last_rc, error);
186 
187  if (last_rc != pcmk_rc_ok) {
188  rc = last_rc;
189  }
190  }
191 
193  return rc;
194 }
195 
196 // Documented in pacemaker.h
197 int
198 pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date,
199  const char **rule_ids)
200 {
201  pcmk__output_t *out = NULL;
202  int rc = pcmk_rc_ok;
203 
204  rc = pcmk__xml_output_new(&out, xml);
205  if (rc != pcmk_rc_ok) {
206  return rc;
207  }
208 
210 
211  rc = pcmk__check_rules(out, input, date, rule_ids);
212  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
213  return rc;
214 }
#define PCMK_XA_YEARS
Definition: xml_names.h:456
int(* message)(pcmk__output_t *out, const char *message_id,...)
struct crm_time_s crm_time_t
Definition: iso8601.h:32
#define PCMK_VALUE_DATE_SPEC
Definition: options.h:141
#define PCMK_XE_CONSTRAINTS
Definition: xml_names.h:89
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition: results.c:704
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition: output.c:271
#define PCMK_XA_OPERATION
Definition: xml_names.h:349
#define PCMK__XA_MOON
int pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, const char **rule_ids)
Definition: pcmk_rule.c:163
#define PCMK_XE_DATE_EXPRESSION
Definition: xml_names.h:96
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
#define PCMK_XE_DATE_SPEC
Definition: xml_names.h:97
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:244
#define XPATH_NODE_RULE
Definition: pcmk_rule.c:22
G_GNUC_INTERNAL int pcmk__init_scheduler(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, pcmk_scheduler_t **scheduler)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:2680
Wrappers for and extensions to libxml2.
ISO_8601 Date handling.
xmlNode * input
Definition: scheduler.h:196
#define pcmk__assert(expr)
void pe_free_working_set(pcmk_scheduler_t *scheduler)
Free scheduler data.
Definition: status.c:50
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path)
Definition: xpath.c:139
int pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date, const char **rule_ids)
Check whether each rule in a list is in effect.
Definition: pcmk_rule.c:198
pcmk_scheduler_t * scheduler
xmlNode * input
enum expression_type pcmk__condition_type(const xmlNode *condition)
Definition: rules.c:37
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition: xpath.c:58
This structure contains everything that makes up a single output formatter.
int pcmk__evaluate_date_expression(const xmlNode *date_expression, const crm_time_t *now, crm_time_t *next_change)
Definition: rules.c:535
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition: xpath.c:39
crm_time_t * now
Definition: scheduler.h:198