This source file includes following definitions.
- interval_cb
- notify_cb
- param_key_val_cb
- test_exit
- test_shutdown
- read_events
- timeout_err
- connection_events
- try_connect
- start_test
- generate_params
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <glib.h>
13 #include <unistd.h>
14
15 #include <crm/crm.h>
16 #include <crm/services.h>
17 #include <crm/common/cmdline_internal.h>
18 #include <crm/common/mainloop.h>
19
20 #include <crm/pengine/status.h>
21 #include <crm/pengine/internal.h>
22 #include <crm/cib.h>
23 #include <crm/cib/internal.h>
24 #include <crm/lrmd.h>
25
26 #define SUMMARY "cts-exec-helper - inject commands into the Pacemaker executor and watch for events"
27
28 static int exec_call_id = 0;
29 static gboolean start_test(gpointer user_data);
30 static void try_connect(void);
31
32 static char *key = NULL;
33 static char *val = NULL;
34
35 static struct {
36 int verbose;
37 int quiet;
38 guint interval_ms;
39 int timeout;
40 int start_delay;
41 int cancel_call_id;
42 gboolean no_wait;
43 gboolean is_running;
44 gboolean no_connect;
45 int exec_call_opts;
46 const char *api_call;
47 const char *rsc_id;
48 const char *provider;
49 const char *class;
50 const char *type;
51 const char *action;
52 const char *listen;
53 gboolean use_tls;
54 lrmd_key_value_t *params;
55 } options;
56
57 static gboolean
58 interval_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
59 return pcmk_parse_interval_spec(optarg,
60 &options.interval_ms) == pcmk_rc_ok;
61 }
62
63 static gboolean
64 notify_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
65 if (pcmk__str_any_of(option_name, "--notify-orig", "-n", NULL)) {
66 options.exec_call_opts = lrmd_opt_notify_orig_only;
67 } else if (pcmk__str_any_of(option_name, "--notify-changes", "-o", NULL)) {
68 options.exec_call_opts = lrmd_opt_notify_changes_only;
69 }
70
71 return TRUE;
72 }
73
74 static gboolean
75 param_key_val_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
76 if (pcmk__str_any_of(option_name, "--param-key", "-k", NULL)) {
77 pcmk__str_update(&key, optarg);
78 } else if (pcmk__str_any_of(option_name, "--param-val", "-v", NULL)) {
79 pcmk__str_update(&val, optarg);
80 }
81
82 if (key != NULL && val != NULL) {
83 options.params = lrmd_key_value_add(options.params, key, val);
84 pcmk__str_update(&key, NULL);
85 pcmk__str_update(&val, NULL);
86 }
87
88 return TRUE;
89 }
90
91 static GOptionEntry basic_entries[] = {
92 { "api-call", 'c', 0, G_OPTION_ARG_STRING, &options.api_call,
93 "Directly relates to executor API functions",
94 NULL },
95
96 { "is-running", 'R', 0, G_OPTION_ARG_NONE, &options.is_running,
97 "Determine if a resource is registered and running",
98 NULL },
99
100 { "listen", 'l', 0, G_OPTION_ARG_STRING, &options.listen,
101 "Listen for a specific event string",
102 NULL },
103
104 { "no-wait", 'w', 0, G_OPTION_ARG_NONE, &options.no_wait,
105 "Make api call and do not wait for result",
106 NULL },
107
108 { "notify-changes", 'o', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, notify_cb,
109 "Only notify client changes to recurring operations",
110 NULL },
111
112 { "notify-orig", 'n', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, notify_cb,
113 "Only notify this client of the results of an API action",
114 NULL },
115
116 { "tls", 'S', 0, G_OPTION_ARG_NONE, &options.use_tls,
117 "Use TLS backend for local connection",
118 NULL },
119
120 { NULL }
121 };
122
123 static GOptionEntry api_call_entries[] = {
124 { "action", 'a', 0, G_OPTION_ARG_STRING, &options.action,
125 NULL, NULL },
126
127 { "cancel-call-id", 'x', 0, G_OPTION_ARG_INT, &options.cancel_call_id,
128 NULL, NULL },
129
130 { "class", 'C', 0, G_OPTION_ARG_STRING, &options.class,
131 NULL, NULL },
132
133 { "interval", 'i', 0, G_OPTION_ARG_CALLBACK, interval_cb,
134 NULL, NULL },
135
136 { "param-key", 'k', 0, G_OPTION_ARG_CALLBACK, param_key_val_cb,
137 NULL, NULL },
138
139 { "param-val", 'v', 0, G_OPTION_ARG_CALLBACK, param_key_val_cb,
140 NULL, NULL },
141
142 { "provider", 'P', 0, G_OPTION_ARG_STRING, &options.provider,
143 NULL, NULL },
144
145 { "rsc-id", 'r', 0, G_OPTION_ARG_STRING, &options.rsc_id,
146 NULL, NULL },
147
148 { "start-delay", 's', 0, G_OPTION_ARG_INT, &options.start_delay,
149 NULL, NULL },
150
151 { "timeout", 't', 0, G_OPTION_ARG_INT, &options.timeout,
152 NULL, NULL },
153
154 { "type", 'T', 0, G_OPTION_ARG_STRING, &options.type,
155 NULL, NULL },
156
157 { NULL }
158 };
159
160 static GMainLoop *mainloop = NULL;
161 static lrmd_t *lrmd_conn = NULL;
162
163 static char event_buf_v0[1024];
164
165 static crm_exit_t
166 test_exit(crm_exit_t exit_code)
167 {
168 lrmd_api_delete(lrmd_conn);
169 return crm_exit(exit_code);
170 }
171
172 #define print_result(fmt, args...) \
173 if (!options.quiet) { \
174 printf(fmt "\n" , ##args); \
175 }
176
177 #define report_event(event) \
178 snprintf(event_buf_v0, sizeof(event_buf_v0), "NEW_EVENT event_type:%s rsc_id:%s action:%s rc:%s op_status:%s", \
179 lrmd_event_type2str(event->type), \
180 event->rsc_id, \
181 event->op_type ? event->op_type : "none", \
182 services_ocf_exitcode_str(event->rc), \
183 pcmk_exec_status_str(event->op_status)); \
184 crm_info("%s", event_buf_v0);
185
186 static void
187 test_shutdown(int nsig)
188 {
189 lrmd_api_delete(lrmd_conn);
190 lrmd_conn = NULL;
191 }
192
193 static void
194 read_events(lrmd_event_data_t * event)
195 {
196 report_event(event);
197 if (options.listen) {
198 if (pcmk__str_eq(options.listen, event_buf_v0, pcmk__str_casei)) {
199 print_result("LISTEN EVENT SUCCESSFUL");
200 test_exit(CRM_EX_OK);
201 }
202 }
203
204 if (exec_call_id && (event->call_id == exec_call_id)) {
205 if (event->op_status == 0 && event->rc == 0) {
206 print_result("API-CALL SUCCESSFUL for 'exec'");
207 } else {
208 print_result("API-CALL FAILURE for 'exec', rc:%d lrmd_op_status:%s",
209 event->rc, pcmk_exec_status_str(event->op_status));
210 test_exit(CRM_EX_ERROR);
211 }
212
213 if (!options.listen) {
214 test_exit(CRM_EX_OK);
215 }
216 }
217 }
218
219 static gboolean
220 timeout_err(gpointer data)
221 {
222 print_result("LISTEN EVENT FAILURE - timeout occurred, never found");
223 test_exit(CRM_EX_TIMEOUT);
224 return FALSE;
225 }
226
227 static void
228 connection_events(lrmd_event_data_t * event)
229 {
230 int rc = event->connection_rc;
231
232 if (event->type != lrmd_event_connect) {
233
234 return;
235 }
236
237 if (!rc) {
238 crm_info("Executor client connection established");
239 start_test(NULL);
240 return;
241 } else {
242 sleep(1);
243 try_connect();
244 crm_notice("Executor client connection failed");
245 }
246 }
247
248 static void
249 try_connect(void)
250 {
251 int tries = 10;
252 static int num_tries = 0;
253 int rc = 0;
254
255 lrmd_conn->cmds->set_callback(lrmd_conn, connection_events);
256 for (; num_tries < tries; num_tries++) {
257 rc = lrmd_conn->cmds->connect_async(lrmd_conn, crm_system_name, 3000);
258
259 if (!rc) {
260 return;
261 }
262 sleep(1);
263 }
264
265 print_result("API CONNECTION FAILURE");
266 test_exit(CRM_EX_ERROR);
267 }
268
269 static gboolean
270 start_test(gpointer user_data)
271 {
272 int rc = 0;
273
274 if (!options.no_connect) {
275 if (!lrmd_conn->cmds->is_connected(lrmd_conn)) {
276 try_connect();
277
278 return 0;
279 }
280 }
281 lrmd_conn->cmds->set_callback(lrmd_conn, read_events);
282
283 if (options.timeout) {
284 g_timeout_add(options.timeout, timeout_err, NULL);
285 }
286
287 if (!options.api_call) {
288 return 0;
289 }
290
291 if (pcmk__str_eq(options.api_call, "exec", pcmk__str_casei)) {
292 rc = lrmd_conn->cmds->exec(lrmd_conn,
293 options.rsc_id,
294 options.action,
295 NULL,
296 options.interval_ms,
297 options.timeout,
298 options.start_delay,
299 options.exec_call_opts,
300 options.params);
301
302 if (rc > 0) {
303 exec_call_id = rc;
304 print_result("API-CALL 'exec' action pending, waiting on response");
305 }
306
307 } else if (pcmk__str_eq(options.api_call, "register_rsc", pcmk__str_casei)) {
308 rc = lrmd_conn->cmds->register_rsc(lrmd_conn,
309 options.rsc_id,
310 options.class, options.provider, options.type, 0);
311 } else if (pcmk__str_eq(options.api_call, "get_rsc_info", pcmk__str_casei)) {
312 lrmd_rsc_info_t *rsc_info;
313
314 rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0);
315
316 if (rsc_info) {
317 print_result("RSC_INFO: id:%s class:%s provider:%s type:%s",
318 rsc_info->id, rsc_info->standard,
319 (rsc_info->provider? rsc_info->provider : "<none>"),
320 rsc_info->type);
321 lrmd_free_rsc_info(rsc_info);
322 rc = pcmk_ok;
323 } else {
324 rc = -1;
325 }
326 } else if (pcmk__str_eq(options.api_call, "unregister_rsc", pcmk__str_casei)) {
327 rc = lrmd_conn->cmds->unregister_rsc(lrmd_conn, options.rsc_id, 0);
328 } else if (pcmk__str_eq(options.api_call, "cancel", pcmk__str_casei)) {
329 rc = lrmd_conn->cmds->cancel(lrmd_conn, options.rsc_id, options.action,
330 options.interval_ms);
331 } else if (pcmk__str_eq(options.api_call, "metadata", pcmk__str_casei)) {
332 char *output = NULL;
333
334 rc = lrmd_conn->cmds->get_metadata(lrmd_conn,
335 options.class,
336 options.provider, options.type, &output, 0);
337 if (rc == pcmk_ok) {
338 print_result("%s", output);
339 free(output);
340 }
341 } else if (pcmk__str_eq(options.api_call, "list_agents", pcmk__str_casei)) {
342 lrmd_list_t *list = NULL;
343 lrmd_list_t *iter = NULL;
344
345 rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, options.class, options.provider);
346
347 if (rc > 0) {
348 print_result("%d agents found", rc);
349 for (iter = list; iter != NULL; iter = iter->next) {
350 print_result("%s", iter->val);
351 }
352 lrmd_list_freeall(list);
353 rc = 0;
354 } else {
355 print_result("API_CALL FAILURE - no agents found");
356 rc = -1;
357 }
358 } else if (pcmk__str_eq(options.api_call, "list_ocf_providers", pcmk__str_casei)) {
359 lrmd_list_t *list = NULL;
360 lrmd_list_t *iter = NULL;
361
362 rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, options.type, &list);
363
364 if (rc > 0) {
365 print_result("%d providers found", rc);
366 for (iter = list; iter != NULL; iter = iter->next) {
367 print_result("%s", iter->val);
368 }
369 lrmd_list_freeall(list);
370 rc = 0;
371 } else {
372 print_result("API_CALL FAILURE - no providers found");
373 rc = -1;
374 }
375
376 } else if (pcmk__str_eq(options.api_call, "list_standards", pcmk__str_casei)) {
377 lrmd_list_t *list = NULL;
378 lrmd_list_t *iter = NULL;
379
380 rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list);
381
382 if (rc > 0) {
383 print_result("%d standards found", rc);
384 for (iter = list; iter != NULL; iter = iter->next) {
385 print_result("%s", iter->val);
386 }
387 lrmd_list_freeall(list);
388 rc = 0;
389 } else {
390 print_result("API_CALL FAILURE - no providers found");
391 rc = -1;
392 }
393
394 } else if (pcmk__str_eq(options.api_call, "get_recurring_ops", pcmk__str_casei)) {
395 GList *op_list = NULL;
396 GList *op_item = NULL;
397 rc = lrmd_conn->cmds->get_recurring_ops(lrmd_conn, options.rsc_id, 0, 0,
398 &op_list);
399
400 for (op_item = op_list; op_item != NULL; op_item = op_item->next) {
401 lrmd_op_info_t *op_info = op_item->data;
402
403 print_result("RECURRING_OP: %s_%s_%s timeout=%sms",
404 op_info->rsc_id, op_info->action,
405 op_info->interval_ms_s, op_info->timeout_ms_s);
406 lrmd_free_op_info(op_info);
407 }
408 g_list_free(op_list);
409
410 } else if (options.api_call) {
411 print_result("API-CALL FAILURE unknown action '%s'", options.action);
412 test_exit(CRM_EX_ERROR);
413 }
414
415 if (rc < 0) {
416 print_result("API-CALL FAILURE for '%s' api_rc:%d",
417 options.api_call, rc);
418 test_exit(CRM_EX_ERROR);
419 }
420
421 if (options.api_call && rc == pcmk_ok) {
422 print_result("API-CALL SUCCESSFUL for '%s'", options.api_call);
423 if (!options.listen) {
424 test_exit(CRM_EX_OK);
425 }
426 }
427
428 if (options.no_wait) {
429
430 test_exit(CRM_EX_OK);
431 }
432
433 return 0;
434 }
435
436
437
438
439
440
441
442 static int
443 generate_params(void)
444 {
445 int rc = pcmk_rc_ok;
446 pcmk_scheduler_t *scheduler = NULL;
447 xmlNode *cib_xml_copy = NULL;
448 pcmk_resource_t *rsc = NULL;
449 GHashTable *params = NULL;
450 GHashTable *meta = NULL;
451 GHashTableIter iter;
452 char *key = NULL;
453 char *value = NULL;
454
455 if (options.params != NULL) {
456 return pcmk_rc_ok;
457 }
458
459
460 rc = cib__signon_query(NULL, NULL, &cib_xml_copy);
461 if (rc != pcmk_rc_ok) {
462 return rc;
463 }
464 rc = pcmk__update_configured_schema(&cib_xml_copy, false);
465 if (rc != pcmk_rc_ok) {
466 return rc;
467 }
468
469
470 scheduler = pe_new_working_set();
471 if (scheduler == NULL) {
472 crm_crit("Could not allocate scheduler data");
473 return ENOMEM;
474 }
475 pcmk__set_scheduler_flags(scheduler,
476 pcmk_sched_no_counts|pcmk_sched_no_compat);
477 scheduler->input = cib_xml_copy;
478 scheduler->now = crm_time_new(NULL);
479 cluster_status(scheduler);
480
481
482 rsc = pe_find_resource_with_flags(scheduler->resources, options.rsc_id,
483 pcmk_rsc_match_history
484 |pcmk_rsc_match_basename);
485 if (rsc == NULL) {
486 crm_err("Resource does not exist in config");
487 pe_free_working_set(scheduler);
488 return EINVAL;
489 }
490
491
492 params = pe_rsc_params(rsc, NULL, scheduler);
493 if (params != NULL) {
494 g_hash_table_iter_init(&iter, params);
495 while (g_hash_table_iter_next(&iter, (gpointer *) &key,
496 (gpointer *) &value)) {
497 options.params = lrmd_key_value_add(options.params, key, value);
498 }
499 }
500
501
502 meta = pcmk__strkey_table(free, free);
503 get_meta_attributes(meta, rsc, NULL, scheduler);
504 g_hash_table_iter_init(&iter, meta);
505 while (g_hash_table_iter_next(&iter, (gpointer *) &key,
506 (gpointer *) &value)) {
507 char *crm_name = crm_meta_name(key);
508
509 options.params = lrmd_key_value_add(options.params, crm_name, value);
510 free(crm_name);
511 }
512 g_hash_table_destroy(meta);
513
514 pe_free_working_set(scheduler);
515 return rc;
516 }
517
518 static GOptionContext *
519 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
520 GOptionContext *context = NULL;
521
522 context = pcmk__build_arg_context(args, NULL, group, NULL);
523
524 pcmk__add_main_args(context, basic_entries);
525 pcmk__add_arg_group(context, "api-call", "API Call Options:",
526 "Parameters for api-call option", api_call_entries);
527
528 return context;
529 }
530
531 int
532 main(int argc, char **argv)
533 {
534 GError *error = NULL;
535 crm_exit_t exit_code = CRM_EX_OK;
536 crm_trigger_t *trig = NULL;
537
538 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
539
540
541
542
543
544 gchar **processed_args = pcmk__cmdline_preproc(argv, NULL);
545 GOptionContext *context = build_arg_context(args, NULL);
546
547 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
548 exit_code = CRM_EX_USAGE;
549 goto done;
550 }
551
552
553
554
555
556 crm_log_init(NULL, LOG_INFO, TRUE, (args->verbosity? TRUE : FALSE), argc,
557 argv, FALSE);
558
559 for (int i = 0; i < args->verbosity; i++) {
560 crm_bump_log_level(argc, argv);
561 }
562
563 if (!options.listen && pcmk__strcase_any_of(options.api_call, "metadata", "list_agents",
564 "list_standards", "list_ocf_providers", NULL)) {
565 options.no_connect = TRUE;
566 }
567
568 if (options.is_running) {
569 int rc = pcmk_rc_ok;
570
571 if (options.rsc_id == NULL) {
572 exit_code = CRM_EX_USAGE;
573 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
574 "--is-running requires --rsc-id");
575 goto done;
576 }
577
578 options.interval_ms = 0;
579 if (options.timeout == 0) {
580 options.timeout = 30000;
581 }
582
583 rc = generate_params();
584 if (rc != pcmk_rc_ok) {
585 exit_code = pcmk_rc2exitc(rc);
586 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
587 "Can not determine resource status: "
588 "unable to get parameters from CIB");
589 goto done;
590 }
591 options.api_call = "exec";
592 options.action = PCMK_ACTION_MONITOR;
593 options.exec_call_opts = lrmd_opt_notify_orig_only;
594 }
595
596 if (!options.api_call && !options.listen) {
597 exit_code = CRM_EX_USAGE;
598 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
599 "Must specify at least one of --api-call, --listen, "
600 "or --is-running");
601 goto done;
602 }
603
604 if (options.use_tls) {
605 lrmd_conn = lrmd_remote_api_new(NULL, "localhost", 0);
606 } else {
607 lrmd_conn = lrmd_api_new();
608 }
609 trig = mainloop_add_trigger(G_PRIORITY_HIGH, start_test, NULL);
610 mainloop_set_trigger(trig);
611 mainloop_add_signal(SIGTERM, test_shutdown);
612
613 crm_info("Starting");
614 mainloop = g_main_loop_new(NULL, FALSE);
615 g_main_loop_run(mainloop);
616
617 done:
618 g_strfreev(processed_args);
619 pcmk__free_arg_context(context);
620
621 free(key);
622 free(val);
623
624 pcmk__output_and_clear_error(&error, NULL);
625 return test_exit(exit_code);
626 }