This source file includes following definitions.
- mode_cb
- eval_date_expression
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- crm_rule_check
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <crm/cib.h>
13 #include <crm/cib/internal.h>
14 #include <crm/common/cmdline_internal.h>
15 #include <crm/common/output_internal.h>
16 #include <crm/common/iso8601.h>
17 #include <crm/msg_xml.h>
18 #include <crm/pengine/rules_internal.h>
19 #include <crm/pengine/status.h>
20 #include <pacemaker-internal.h>
21
22 #include <sys/stat.h>
23
24 #define SUMMARY "evaluate rules from the Pacemaker configuration"
25
26 GError *error = NULL;
27
28 static pcmk__supported_format_t formats[] = {
29 PCMK__SUPPORTED_FORMAT_NONE,
30 PCMK__SUPPORTED_FORMAT_TEXT,
31 PCMK__SUPPORTED_FORMAT_XML,
32 { NULL, NULL, NULL }
33 };
34
35 enum crm_rule_mode {
36 crm_rule_mode_none,
37 crm_rule_mode_check
38 };
39
40 struct {
41 char *date;
42 char *input_xml;
43 enum crm_rule_mode mode;
44 gchar **rules;
45 } options = {
46 .mode = crm_rule_mode_none
47 };
48
49 static int crm_rule_check(pcmk__output_t *out, pe_working_set_t *data_set,
50 const char *rule_id, crm_time_t *effective_date);
51
52 static gboolean mode_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
53
54 static GOptionEntry mode_entries[] = {
55 { "check", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, mode_cb,
56 "Check whether a rule is in effect",
57 NULL },
58
59 { NULL }
60 };
61
62 static GOptionEntry data_entries[] = {
63 { "xml-text", 'X', 0, G_OPTION_ARG_STRING, &options.input_xml,
64 "Use argument for XML (or stdin if '-')",
65 NULL },
66
67 { NULL }
68 };
69
70 static GOptionEntry addl_entries[] = {
71 { "date", 'd', 0, G_OPTION_ARG_STRING, &options.date,
72 "Whether the rule is in effect on a given date",
73 NULL },
74 { "rule", 'r', 0, G_OPTION_ARG_STRING_ARRAY, &options.rules,
75 "The ID of the rule to check (may be specified multiple times)",
76 NULL },
77
78 { NULL }
79 };
80
81 static gboolean
82 mode_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
83 if (strcmp(option_name, "c")) {
84 options.mode = crm_rule_mode_check;
85 }
86
87 return TRUE;
88 }
89
90
91
92
93
94
95
96
97
98
99
100 static int
101 eval_date_expression(xmlNode *expr, crm_time_t *now, crm_time_t *next_change)
102 {
103 pe_rule_eval_data_t rule_data = {
104 .node_hash = NULL,
105 .role = RSC_ROLE_UNKNOWN,
106 .now = now,
107 .match_data = NULL,
108 .rsc_data = NULL,
109 .op_data = NULL
110 };
111
112 return pe__eval_date_expr(expr, &rule_data, next_change);
113 }
114
115 PCMK__OUTPUT_ARGS("rule-check", "const char *", "int")
116 static int
117 rule_check_default(pcmk__output_t *out, va_list args)
118 {
119 const char *rule_id = va_arg(args, const char *);
120 int result = va_arg(args, int);
121
122 if (result == pcmk_rc_within_range) {
123 out->info(out, "Rule %s is still in effect", rule_id);
124 } else if (result == pcmk_rc_ok) {
125 out->info(out, "Rule %s satisfies conditions", rule_id);
126 } else if (result == pcmk_rc_after_range) {
127 out->info(out, "Rule %s is expired", rule_id);
128 } else if (result == pcmk_rc_before_range) {
129 out->info(out, "Rule %s has not yet taken effect", rule_id);
130 } else if (result == pcmk_rc_op_unsatisfied) {
131 out->info(out, "Rule %s does not satisfy conditions", rule_id);
132 } else {
133 out->info(out, "Could not determine whether rule %s is expired", rule_id);
134 }
135
136 return pcmk_rc_ok;
137 }
138
139 PCMK__OUTPUT_ARGS("rule-check", "const char *", "int")
140 static int
141 rule_check_xml(pcmk__output_t *out, va_list args)
142 {
143 const char *rule_id = va_arg(args, const char *);
144 int result = va_arg(args, int);
145
146 char *rc_str = pcmk__itoa(pcmk_rc2exitc(result));
147
148 pcmk__output_create_xml_node(out, "rule-check",
149 "rule-id", rule_id,
150 "rc", rc_str,
151 NULL);
152
153 free(rc_str);
154
155 return pcmk_rc_ok;
156 }
157
158 static pcmk__message_entry_t fmt_functions[] = {
159 { "rule-check", "default", rule_check_default },
160 { "rule-check", "xml", rule_check_xml },
161
162 { NULL, NULL, NULL }
163 };
164
165 static int
166 crm_rule_check(pcmk__output_t *out, pe_working_set_t *data_set, const char *rule_id,
167 crm_time_t *effective_date)
168 {
169 xmlNode *cib_constraints = NULL;
170 xmlNode *match = NULL;
171 xmlXPathObjectPtr xpathObj = NULL;
172 char *xpath = NULL;
173 int rc = pcmk_rc_ok;
174 int max = 0;
175
176
177 cib_constraints = pcmk_find_cib_element(data_set->input,
178 XML_CIB_TAG_CONSTRAINTS);
179
180
181
182
183
184
185
186
187
188 xpath = crm_strdup_printf("//rule[@id='%s']", rule_id);
189 xpathObj = xpath_search(cib_constraints, xpath);
190 max = numXpathResults(xpathObj);
191
192 if (max == 0) {
193 rc = ENXIO;
194 g_set_error(&error, PCMK__RC_ERROR, rc, "No rule found with ID=%s", rule_id);
195 goto done;
196 } else if (max > 1) {
197 rc = ENXIO;
198 g_set_error(&error, PCMK__RC_ERROR, rc, "More than one rule with ID=%s found", rule_id);
199 goto done;
200 }
201
202 free(xpath);
203 freeXpathObject(xpathObj);
204
205
206 xpath = crm_strdup_printf("//rule[@id='%s']//date_expression", rule_id);
207 xpathObj = xpath_search(cib_constraints, xpath);
208 max = numXpathResults(xpathObj);
209
210 if (max != 1) {
211 rc = EOPNOTSUPP;
212 g_set_error(&error, PCMK__RC_ERROR, rc,
213 "Can't check rule %s because it does not have exactly one date_expression", rule_id);
214 goto done;
215 }
216
217 free(xpath);
218 freeXpathObject(xpathObj);
219
220
221 xpath = crm_strdup_printf("//rule[@id='%s']//date_expression[@operation!='date_spec']", rule_id);
222 xpathObj = xpath_search(cib_constraints, xpath);
223 max = numXpathResults(xpathObj);
224
225 if (max == 0) {
226 free(xpath);
227 freeXpathObject(xpathObj);
228
229 xpath = crm_strdup_printf("//rule[@id='%s']//date_expression[@operation='date_spec' and date_spec/@years and not(date_spec/@moon)]",
230 rule_id);
231 xpathObj = xpath_search(cib_constraints, xpath);
232 max = numXpathResults(xpathObj);
233
234 if (max == 0) {
235 rc = ENXIO;
236 g_set_error(&error, PCMK__RC_ERROR, rc,
237 "Rule either must not use date_spec, or use date_spec with years= but not moon=");
238 goto done;
239 }
240 }
241
242 match = getXpathResult(xpathObj, 0);
243
244
245
246
247 CRM_ASSERT(match != NULL);
248 CRM_ASSERT(find_expression_type(match) == time_expr);
249
250 rc = eval_date_expression(match, effective_date, NULL);
251 out->message(out, "rule-check", rule_id, rc);
252
253 done:
254 free(xpath);
255 freeXpathObject(xpathObj);
256 return rc;
257 }
258
259 static GOptionContext *
260 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
261 GOptionContext *context = NULL;
262
263 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
264
265 pcmk__add_arg_group(context, "modes", "Modes (mutually exclusive):",
266 "Show modes of operation", mode_entries);
267 pcmk__add_arg_group(context, "data", "Data:",
268 "Show data options", data_entries);
269 pcmk__add_arg_group(context, "additional", "Additional Options:",
270 "Show additional options", addl_entries);
271 return context;
272 }
273
274 int
275 main(int argc, char **argv)
276 {
277 pe_working_set_t *data_set = NULL;
278
279 crm_time_t *rule_date = NULL;
280 xmlNode *input = NULL;
281
282 int rc = pcmk_rc_ok;
283 crm_exit_t exit_code = CRM_EX_OK;
284
285 pcmk__output_t *out = NULL;
286
287 GOptionGroup *output_group = NULL;
288 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
289 GOptionContext *context = build_arg_context(args, &output_group);
290 gchar **processed_args = pcmk__cmdline_preproc(argv, "drX");
291
292 pcmk__register_formats(output_group, formats);
293 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
294 exit_code = CRM_EX_USAGE;
295 goto done;
296 }
297
298 pcmk__cli_init_logging("crm_rule", args->verbosity);
299
300 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
301 if (rc != pcmk_rc_ok) {
302 exit_code = CRM_EX_ERROR;
303 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
304 args->output_ty, pcmk_rc_str(rc));
305 goto done;
306 }
307
308 pcmk__register_messages(out, fmt_functions);
309
310 if (args->version) {
311 out->version(out, false);
312 goto done;
313 }
314
315
316
317
318 switch(options.mode) {
319 case crm_rule_mode_check:
320 if (options.rules == NULL) {
321 exit_code = CRM_EX_USAGE;
322 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "--check requires use of --rule=");
323 goto done;
324 }
325
326 break;
327
328 default:
329 exit_code = CRM_EX_USAGE;
330 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "No mode operation given");
331 goto done;
332 break;
333 }
334
335
336 rule_date = crm_time_new(options.date);
337 if (rule_date == NULL) {
338 exit_code = CRM_EX_DATAERR;
339 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
340 "No --date given and can't determine current date");
341 goto done;
342 }
343
344
345
346
347 if (pcmk__str_eq(options.input_xml, "-", pcmk__str_casei)) {
348 input = stdin2xml();
349
350 if (input == NULL) {
351 exit_code = CRM_EX_DATAERR;
352 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Couldn't parse input from STDIN\n");
353 goto done;
354 }
355 } else if (options.input_xml != NULL) {
356 input = string2xml(options.input_xml);
357
358 if (input == NULL) {
359 exit_code = CRM_EX_DATAERR;
360 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
361 "Couldn't parse input string: %s\n", options.input_xml);
362 goto done;
363 }
364 } else {
365 rc = cib__signon_query(NULL, &input);
366
367 if (rc != pcmk_rc_ok) {
368 exit_code = pcmk_rc2exitc(rc);
369 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
370 "CIB query failed: %s", pcmk_rc_str(rc));
371 goto done;
372 }
373 }
374
375
376 data_set = pe_new_working_set();
377 if (data_set == NULL) {
378 exit_code = crm_errno2exit(ENOMEM);
379 goto done;
380 }
381 pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat);
382
383 data_set->input = input;
384 data_set->now = rule_date;
385
386
387 cluster_status(data_set);
388
389
390
391
392
393 switch(options.mode) {
394 case crm_rule_mode_check:
395 for (char **s = options.rules; *s != NULL; s++) {
396 int last_rc = crm_rule_check(out, data_set, *s, rule_date);
397
398 if (last_rc != pcmk_rc_ok) {
399 rc = last_rc;
400 }
401 }
402
403 exit_code = pcmk_rc2exitc(rc);
404 break;
405
406 default:
407 break;
408 }
409
410 done:
411 g_strfreev(processed_args);
412 pcmk__free_arg_context(context);
413 pe_free_working_set(data_set);
414
415 pcmk__output_and_clear_error(error, out);
416
417 if (out != NULL) {
418 out->finish(out, exit_code, true, NULL);
419 pcmk__output_free(out);
420 }
421
422 return crm_exit(exit_code);
423 }