pacemaker 2.1.8-2.1.8
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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>
18#include <pacemaker-internal.h>
19
21
22#define XPATH_NODE_RULE "//" PCMK_XE_RULE "[@" PCMK_XA_ID "='%s']"
23
34static int
35eval_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";
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
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 CRM_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
162int
163pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date,
164 const char **rule_ids)
165{
167 int rc = pcmk_rc_ok;
168
169 CRM_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
197int
198pcmk_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);
213 return rc;
214}
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition cib.c:172
@ pcmk__condition_datetime
Definition rules.h:38
int pcmk__evaluate_date_expression(const xmlNode *date_expression, const crm_time_t *now, crm_time_t *next_change)
Definition rules.c:534
enum expression_type pcmk__condition_type(const xmlNode *condition)
Definition rules.c:37
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
ISO_8601 Date handling.
struct crm_time_s crm_time_t
Definition iso8601.h:32
G_GNUC_INTERNAL int pcmk__init_scheduler(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, pcmk_scheduler_t **scheduler)
pcmk_scheduler_t * scheduler
xmlNode * input
#define PCMK_VALUE_DATE_SPEC
Definition options.h:141
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition output.c:271
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition output.c:244
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 XPATH_NODE_RULE
Definition pcmk_rule.c:22
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
void pcmk__register_lib_messages(pcmk__output_t *out)
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:162
@ pcmk_rc_undetermined
Definition results.h:135
@ pcmk_rc_duplicate_id
Definition results.h:124
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition results.c:702
void pe_free_working_set(pcmk_scheduler_t *scheduler)
Free scheduler data.
Definition status.c:50
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
xmlNode * input
Definition scheduler.h:196
crm_time_t * now
Definition scheduler.h:198
Wrappers for and extensions to libxml2.
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition xpath.c:58
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition xpath.c:39
xmlXPathObjectPtr xpath_search(const xmlNode *xml_top, const char *path)
Definition xpath.c:139
#define PCMK_XE_CONSTRAINTS
Definition xml_names.h:89
#define PCMK_XA_YEARS
Definition xml_names.h:451
#define PCMK_XA_OPERATION
Definition xml_names.h:344
#define PCMK_XE_DATE_SPEC
Definition xml_names.h:96
#define PCMK_XE_DATE_EXPRESSION
Definition xml_names.h:95
#define PCMK__XA_MOON