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