pacemaker 2.1.6-6fdc9deea29
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_rule.c
Go to the documentation of this file.
1/*
2 * Copyright 2022-2023 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/msg_xml.h>
17#include <pacemaker-internal.h>
18
28static int
29eval_date_expression(const xmlNode *expr, crm_time_t *now)
30{
31 pe_rule_eval_data_t rule_data = {
32 .node_hash = NULL,
33 .role = RSC_ROLE_UNKNOWN,
34 .now = now,
35 .match_data = NULL,
36 .rsc_data = NULL,
37 .op_data = NULL
38 };
39
40 return pe__eval_date_expr(expr, &rule_data, NULL);
41}
42
59static int
60init_rule_check(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date,
62{
63 // Allows for cleaner syntax than dereferencing the data_set argument
64 pe_working_set_t *new_data_set = NULL;
65
66 new_data_set = pe_new_working_set();
67 if (new_data_set == NULL) {
68 return ENOMEM;
69 }
70
71 pe__set_working_set_flags(new_data_set,
73
74 // Populate the working set instance
75
76 // Make our own copy of the given input or fetch the CIB and use that
77 if (input != NULL) {
78 new_data_set->input = copy_xml(input);
79 if (new_data_set->input == NULL) {
80 out->err(out, "Failed to copy input XML");
81 pe_free_working_set(new_data_set);
82 return ENOMEM;
83 }
84
85 } else {
86 int rc = cib__signon_query(out, NULL, &(new_data_set->input));
87
88 if (rc != pcmk_rc_ok) {
89 pe_free_working_set(new_data_set);
90 return rc;
91 }
92 }
93
94 // Make our own copy of the given crm_time_t object; otherwise
95 // cluster_status() populates with the current time
96 if (date != NULL) {
97 // pcmk_copy_time() guarantees non-NULL
98 new_data_set->now = pcmk_copy_time(date);
99 }
100
101 // Unpack everything
102 cluster_status(new_data_set);
103 *data_set = new_data_set;
104
105 return pcmk_rc_ok;
106}
107
108#define XPATH_NODE_RULE "//" XML_TAG_RULE "[@" XML_ATTR_ID "='%s']"
109
120static int
121eval_rule(pe_working_set_t *data_set, const char *rule_id, const char **error)
122{
123 xmlNodePtr cib_constraints = NULL;
124 xmlNodePtr match = NULL;
125 xmlXPathObjectPtr xpath_obj = NULL;
126 char *xpath = NULL;
127 int rc = pcmk_rc_ok;
128 int num_results = 0;
129
130 *error = NULL;
131
132 /* Rules are under the constraints node in the XML, so first find that. */
133 cib_constraints = pcmk_find_cib_element(data_set->input,
135
136 /* Get all rules matching the given ID that are also simple enough for us
137 * to check. For the moment, these rules must only have a single
138 * date_expression child and:
139 * - Do not have a date_spec operation, or
140 * - Have a date_spec operation that contains years= but does not contain
141 * moon=.
142 *
143 * We do this in steps to provide better error messages. First, check that
144 * there's any rule with the given ID.
145 */
146 xpath = crm_strdup_printf(XPATH_NODE_RULE, rule_id);
147 xpath_obj = xpath_search(cib_constraints, xpath);
148 num_results = numXpathResults(xpath_obj);
149
150 free(xpath);
151 freeXpathObject(xpath_obj);
152
153 if (num_results == 0) {
154 *error = "Rule not found";
155 return ENXIO;
156 }
157
158 if (num_results > 1) {
159 // Should not be possible; schema prevents this
160 *error = "Found more than one rule with matching ID";
162 }
163
164 /* Next, make sure it has exactly one date_expression. */
165 xpath = crm_strdup_printf(XPATH_NODE_RULE "//date_expression", rule_id);
166 xpath_obj = xpath_search(cib_constraints, xpath);
167 num_results = numXpathResults(xpath_obj);
168
169 free(xpath);
170 freeXpathObject(xpath_obj);
171
172 if (num_results != 1) {
173 if (num_results == 0) {
174 *error = "Rule does not have a date expression";
175 } else {
176 *error = "Rule has more than one date expression";
177 }
178 return EOPNOTSUPP;
179 }
180
181 /* Then, check that it's something we actually support. */
182 xpath = crm_strdup_printf(XPATH_NODE_RULE "//date_expression["
183 "@" XML_EXPR_ATTR_OPERATION "!='date_spec']",
184 rule_id);
185 xpath_obj = xpath_search(cib_constraints, xpath);
186 num_results = numXpathResults(xpath_obj);
187
188 free(xpath);
189
190 if (num_results == 0) {
191 freeXpathObject(xpath_obj);
192
193 xpath = crm_strdup_printf(XPATH_NODE_RULE "//date_expression["
194 "@" XML_EXPR_ATTR_OPERATION "='date_spec' "
195 "and date_spec/@years "
196 "and not(date_spec/@moon)]", rule_id);
197 xpath_obj = xpath_search(cib_constraints, xpath);
198 num_results = numXpathResults(xpath_obj);
199
200 free(xpath);
201
202 if (num_results == 0) {
203 freeXpathObject(xpath_obj);
204 *error = "Rule must either not use date_spec, or use date_spec "
205 "with years= but not moon=";
206 return EOPNOTSUPP;
207 }
208 }
209
210 match = getXpathResult(xpath_obj, 0);
211
212 /* We should have ensured this with the xpath query above, but double-
213 * checking can't hurt.
214 */
215 CRM_ASSERT(match != NULL);
217
218 rc = eval_date_expression(match, data_set->now);
219 if (rc == pcmk_rc_undetermined) {
220 /* pe__eval_date_expr() should return this only if something is
221 * malformed or missing
222 */
223 *error = "Error parsing rule";
224 }
225
226 freeXpathObject(xpath_obj);
227 return rc;
228}
229
243int
244pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date,
245 const char **rule_ids)
246{
248 int rc = pcmk_rc_ok;
249
250 CRM_ASSERT(out != NULL);
251
252 if (rule_ids == NULL) {
253 // Trivial case; every rule specified is in effect
254 return pcmk_rc_ok;
255 }
256
257 rc = init_rule_check(out, input, date, &data_set);
258 if (rc != pcmk_rc_ok) {
259 return rc;
260 }
261
262 for (const char **rule_id = rule_ids; *rule_id != NULL; rule_id++) {
263 const char *error = NULL;
264 int last_rc = eval_rule(data_set, *rule_id, &error);
265
266 out->message(out, "rule-check", *rule_id, last_rc, error);
267
268 if (last_rc != pcmk_rc_ok) {
269 rc = last_rc;
270 }
271 }
272
274 return rc;
275}
276
277// Documented in pacemaker.h
278int
279pcmk_check_rules(xmlNodePtr *xml, xmlNodePtr input, const crm_time_t *date,
280 const char **rule_ids)
281{
282 pcmk__output_t *out = NULL;
283 int rc = pcmk_rc_ok;
284
285 rc = pcmk__xml_output_new(&out, xml);
286 if (rc != pcmk_rc_ok) {
287 return rc;
288 }
289
291
292 rc = pcmk__check_rules(out, input, date, rule_ids);
293 pcmk__xml_output_finish(out, xml);
294 return rc;
295}
int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
Definition cib_utils.c:745
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition cib.c:153
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
@ RSC_ROLE_UNKNOWN
Definition common.h:93
ISO_8601 Date handling.
crm_time_t * pcmk_copy_time(const crm_time_t *source)
Definition iso8601.c:1374
struct crm_time_s crm_time_t
Definition iso8601.h:32
#define XML_EXPR_ATTR_OPERATION
Definition msg_xml.h:358
#define XML_CIB_TAG_CONSTRAINTS
Definition msg_xml.h:202
pe_working_set_t * data_set
xmlNode * input
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition output.c:236
void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml)
Definition output.c:258
int pcmk__check_rules(pcmk__output_t *out, xmlNodePtr input, const crm_time_t *date, const char **rule_ids)
Definition pcmk_rule.c:244
#define XPATH_NODE_RULE
Definition pcmk_rule.c:108
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:279
void pcmk__register_lib_messages(pcmk__output_t *out)
#define pe_flag_no_compat
Definition pe_types.h:148
#define pe_flag_no_counts
Don't count total, disabled and blocked resource instances.
Definition pe_types.h:143
#define pe__set_working_set_flags(working_set, flags_to_set)
Definition internal.h:65
#define CRM_ASSERT(expr)
Definition results.h:42
@ pcmk_rc_ok
Definition results.h:151
@ pcmk_rc_undetermined
Definition results.h:125
@ pcmk_rc_duplicate_id
Definition results.h:114
enum expression_type find_expression_type(xmlNode *expr)
Definition rules.c:105
@ time_expr
Definition rules.h:28
int pe__eval_date_expr(const xmlNode *expr, const pe_rule_eval_data_t *rule_data, crm_time_t *next_change)
Definition rules.c:1033
gboolean cluster_status(pe_working_set_t *data_set)
Definition status.c:71
void pe_free_working_set(pe_working_set_t *data_set)
Free a working set.
Definition status.c:50
pe_working_set_t * pe_new_working_set(void)
Create a new working set.
Definition status.c:34
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
int(*) int(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
GHashTable * node_hash
Definition common.h:193
xmlNode * input
Definition pe_types.h:160
crm_time_t * now
Definition pe_types.h:161
xmlXPathObjectPtr xpath_search(xmlNode *xml_top, const char *path)
Definition xpath.c:139
xmlNode * getXpathResult(xmlXPathObjectPtr xpathObj, int index)
Definition xpath.c:58
void freeXpathObject(xmlXPathObjectPtr xpathObj)
Definition xpath.c:39
xmlNode * copy_xml(xmlNode *src_node)
Definition xml.c:819