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