This source file includes following definitions.
- mode_cb
- eval_date_expression
- 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 enum crm_rule_mode {
27 crm_rule_mode_none,
28 crm_rule_mode_check
29 };
30
31 struct {
32 char *date;
33 char *input_xml;
34 enum crm_rule_mode mode;
35 char *rule;
36 } options = {
37 .mode = crm_rule_mode_none
38 };
39
40 static int crm_rule_check(pe_working_set_t *data_set, const char *rule_id, crm_time_t *effective_date);
41
42 static gboolean mode_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error);
43
44 static GOptionEntry mode_entries[] = {
45 { "check", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, mode_cb,
46 "Check whether a rule is in effect",
47 NULL },
48
49 { NULL }
50 };
51
52 static GOptionEntry data_entries[] = {
53 { "xml-text", 'X', 0, G_OPTION_ARG_STRING, &options.input_xml,
54 "Use argument for XML (or stdin if '-')",
55 NULL },
56
57 { NULL }
58 };
59
60 static GOptionEntry addl_entries[] = {
61 { "date", 'd', 0, G_OPTION_ARG_STRING, &options.date,
62 "Whether the rule is in effect on a given date",
63 NULL },
64 { "rule", 'r', 0, G_OPTION_ARG_STRING, &options.rule,
65 "The ID of the rule to check",
66 NULL },
67
68 { NULL }
69 };
70
71 static gboolean
72 mode_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
73 if (strcmp(option_name, "c")) {
74 options.mode = crm_rule_mode_check;
75 }
76
77 return TRUE;
78 }
79
80
81
82
83
84
85
86
87
88
89
90 static int
91 eval_date_expression(xmlNode *expr, crm_time_t *now, crm_time_t *next_change)
92 {
93 pe_rule_eval_data_t rule_data = {
94 .node_hash = NULL,
95 .role = RSC_ROLE_UNKNOWN,
96 .now = now,
97 .match_data = NULL,
98 .rsc_data = NULL,
99 .op_data = NULL
100 };
101
102 return pe__eval_date_expr(expr, &rule_data, next_change);
103 }
104
105 static int
106 crm_rule_check(pe_working_set_t *data_set, const char *rule_id, crm_time_t *effective_date)
107 {
108 xmlNode *cib_constraints = NULL;
109 xmlNode *match = NULL;
110 xmlXPathObjectPtr xpathObj = NULL;
111 char *xpath = NULL;
112 int rc = pcmk_rc_ok;
113 int max = 0;
114
115
116 cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input);
117
118
119
120
121
122
123
124
125
126 xpath = crm_strdup_printf("//rule[@id='%s']", rule_id);
127 xpathObj = xpath_search(cib_constraints, xpath);
128 max = numXpathResults(xpathObj);
129
130 if (max == 0) {
131 CMD_ERR("No rule found with ID=%s", rule_id);
132 rc = ENXIO;
133 goto done;
134 } else if (max > 1) {
135 CMD_ERR("More than one rule with ID=%s found", rule_id);
136 rc = ENXIO;
137 goto done;
138 }
139
140 free(xpath);
141 freeXpathObject(xpathObj);
142
143
144 xpath = crm_strdup_printf("//rule[@id='%s']//date_expression", rule_id);
145 xpathObj = xpath_search(cib_constraints, xpath);
146 max = numXpathResults(xpathObj);
147
148 if (max != 1) {
149 CMD_ERR("Can't check rule %s because it does not have exactly one date_expression", rule_id);
150 rc = EOPNOTSUPP;
151 goto done;
152 }
153
154 free(xpath);
155 freeXpathObject(xpathObj);
156
157
158 xpath = crm_strdup_printf("//rule[@id='%s']//date_expression[@operation!='date_spec']", rule_id);
159 xpathObj = xpath_search(cib_constraints, xpath);
160 max = numXpathResults(xpathObj);
161
162 if (max == 0) {
163 free(xpath);
164 freeXpathObject(xpathObj);
165
166 xpath = crm_strdup_printf("//rule[@id='%s']//date_expression[@operation='date_spec' and date_spec/@years and not(date_spec/@moon)]",
167 rule_id);
168 xpathObj = xpath_search(cib_constraints, xpath);
169 max = numXpathResults(xpathObj);
170
171 if (max == 0) {
172 CMD_ERR("Rule either must not use date_spec, or use date_spec with years= but not moon=");
173 rc = ENXIO;
174 goto done;
175 }
176 }
177
178 match = getXpathResult(xpathObj, 0);
179
180
181
182
183 CRM_ASSERT(match != NULL);
184 CRM_ASSERT(find_expression_type(match) == time_expr);
185
186 rc = eval_date_expression(match, effective_date, NULL);
187
188 if (rc == pcmk_rc_within_range) {
189 printf("Rule %s is still in effect\n", rule_id);
190 rc = pcmk_rc_ok;
191 } else if (rc == pcmk_rc_ok) {
192 printf("Rule %s satisfies conditions\n", rule_id);
193 } else if (rc == pcmk_rc_after_range) {
194 printf("Rule %s is expired\n", rule_id);
195 } else if (rc == pcmk_rc_before_range) {
196 printf("Rule %s has not yet taken effect\n", rule_id);
197 } else if (rc == pcmk_rc_op_unsatisfied) {
198 printf("Rule %s does not satisfy conditions\n", rule_id);
199 } else {
200 printf("Could not determine whether rule %s is expired\n", rule_id);
201 }
202
203 done:
204 free(xpath);
205 freeXpathObject(xpathObj);
206 return rc;
207 }
208
209 static GOptionContext *
210 build_arg_context(pcmk__common_args_t *args) {
211 GOptionContext *context = NULL;
212
213 const char *description = "This tool is currently experimental.\n"
214 "The interface, behavior, and output may change "
215 "with any version of Pacemaker.";
216
217 context = pcmk__build_arg_context(args, NULL, NULL, NULL);
218 g_option_context_set_description(context, description);
219
220 pcmk__add_arg_group(context, "modes", "Modes (mutually exclusive):",
221 "Show modes of operation", mode_entries);
222 pcmk__add_arg_group(context, "data", "Data:",
223 "Show data options", data_entries);
224 pcmk__add_arg_group(context, "additional", "Additional Options:",
225 "Show additional options", addl_entries);
226 return context;
227 }
228
229 int
230 main(int argc, char **argv)
231 {
232 pe_working_set_t *data_set = NULL;
233
234 crm_time_t *rule_date = NULL;
235 xmlNode *input = NULL;
236
237 int rc = pcmk_ok;
238 crm_exit_t exit_code = CRM_EX_OK;
239 GError *error = NULL;
240
241 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
242 GOptionContext *context = build_arg_context(args);
243 gchar **processed_args = pcmk__cmdline_preproc(argv, "drX");
244
245 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
246 exit_code = CRM_EX_USAGE;
247 goto done;
248 }
249
250 pcmk__cli_init_logging("crm_rule", args->verbosity);
251
252 if (args->version) {
253 g_strfreev(processed_args);
254 pcmk__free_arg_context(context);
255
256 pcmk__cli_help('v', CRM_EX_OK);
257 }
258
259
260
261
262 switch(options.mode) {
263 case crm_rule_mode_check:
264 if (options.rule == NULL) {
265 CMD_ERR("--check requires use of --rule=");
266 exit_code = CRM_EX_USAGE;
267 goto done;
268 }
269
270 break;
271
272 default:
273 CMD_ERR("No mode operation given");
274 exit_code = CRM_EX_USAGE;
275 goto done;
276 break;
277 }
278
279
280 rule_date = crm_time_new(options.date);
281 if (rule_date == NULL) {
282 CMD_ERR("No --date given and can't determine current date");
283 exit_code = CRM_EX_DATAERR;
284 goto done;
285 }
286
287
288
289
290 if (pcmk__str_eq(options.input_xml, "-", pcmk__str_casei)) {
291 input = stdin2xml();
292
293 if (input == NULL) {
294 CMD_ERR("Couldn't parse input from STDIN\n");
295 exit_code = CRM_EX_DATAERR;
296 goto done;
297 }
298 } else if (options.input_xml != NULL) {
299 input = string2xml(options.input_xml);
300
301 if (input == NULL) {
302 CMD_ERR("Couldn't parse input string: %s\n", options.input_xml);
303
304 exit_code = CRM_EX_DATAERR;
305 goto done;
306 }
307 } else {
308 rc = cib__signon_query(NULL, &input);
309
310 if (rc != pcmk_rc_ok) {
311 CMD_ERR("CIB query failed: %s", pcmk_rc_str(rc));
312 exit_code = pcmk_rc2exitc(rc);
313 goto done;
314 }
315 }
316
317
318 data_set = pe_new_working_set();
319 if (data_set == NULL) {
320 exit_code = crm_errno2exit(ENOMEM);
321 goto done;
322 }
323 pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat);
324
325 data_set->input = input;
326 data_set->now = rule_date;
327
328
329 cluster_status(data_set);
330
331
332
333
334
335 switch(options.mode) {
336 case crm_rule_mode_check:
337 rc = crm_rule_check(data_set, options.rule, rule_date);
338
339 if (rc > 0) {
340 CMD_ERR("Error checking rule: %s", pcmk_rc_str(rc));
341 }
342
343 exit_code = pcmk_rc2exitc(rc);
344 break;
345
346 default:
347 break;
348 }
349
350 done:
351 g_strfreev(processed_args);
352 pcmk__free_arg_context(context);
353 pe_free_working_set(data_set);
354
355 pcmk__output_and_clear_error(error, NULL);
356 crm_exit(exit_code);
357 }