1 /*
2 * Copyright (c) 2012 David Vossel <davidvossel@gmail.com>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 *
18 */
19
20 #include <crm_internal.h>
21
22 #include <glib.h>
23 #include <unistd.h>
24
25 #include <crm/crm.h>
26 #include <crm/services.h>
27 #include <crm/common/mainloop.h>
28
29 #include <crm/pengine/status.h>
30 #include <crm/cib.h>
31 #include <crm/lrmd.h>
32
33 /* *INDENT-OFF* */
34 static struct crm_option long_options[] = {
35 {"help", 0, 0, '?'},
36 {"verbose", 0, 0, 'V', "\t\tPrint out logs and events to screen"},
37 {"quiet", 0, 0, 'Q', "\t\tSuppress all output to screen"},
38 {"tls", 0, 0, 'S', "\t\tUse tls backend for local connection"},
39 {"listen", 1, 0, 'l', "\tListen for a specific event string"},
40 {"api-call", 1, 0, 'c', "\tDirectly relates to lrmd api functions"},
41 {"no-wait", 0, 0, 'w', "\tMake api call and do not wait for result."},
42 {"is-running", 0, 0, 'R', "\tDetermine if a resource is registered and running."},
43 {"notify-orig", 0, 0, 'n', "\tOnly notify this client the results of an api action."},
44 {"notify-changes", 0, 0, 'o', "\tOnly notify client changes to recurring operations."},
45 {"-spacer-", 1, 0, '-', "\nParameters for api-call option"},
46 {"action", 1, 0, 'a'},
47 {"rsc-id", 1, 0, 'r'},
48 {"cancel-call-id", 1, 0, 'x'},
49 {"provider", 1, 0, 'P'},
50 {"class", 1, 0, 'C'},
51 {"type", 1, 0, 'T'},
52 {"interval", 1, 0, 'i'},
53 {"timeout", 1, 0, 't'},
54 {"start-delay", 1, 0, 's'},
55 {"param-key", 1, 0, 'k'},
56 {"param-val", 1, 0, 'v'},
57
58 {"-spacer-", 1, 0, '-'},
59 {0, 0, 0, 0}
60 };
61 /* *INDENT-ON* */
62
63 cib_t *cib_conn = NULL;
64 static int exec_call_id = 0;
65 static int exec_call_opts = 0;
66 static gboolean start_test(gpointer user_data);
67 static void try_connect(void);
68
69 static struct {
70 int verbose;
71 int quiet;
72 int print;
73 int interval;
74 int timeout;
75 int start_delay;
76 int cancel_call_id;
77 int no_wait;
78 int is_running;
79 int no_connect;
80 const char *api_call;
81 const char *rsc_id;
82 const char *provider;
83 const char *class;
84 const char *type;
85 const char *action;
86 const char *listen;
87 lrmd_key_value_t *params;
88 } options;
89
90 GMainLoop *mainloop = NULL;
91 lrmd_t *lrmd_conn = NULL;
92
93 static char event_buf_v0[1024];
94
95 static void
test_exit(int rc)96 test_exit(int rc)
97 {
98 lrmd_api_delete(lrmd_conn);
99 crm_exit(rc);
100 }
101
102 #define print_result(result) \
103 if (!options.quiet) { \
104 result; \
105 } \
106
107 #define report_event(event) \
108 snprintf(event_buf_v0, sizeof(event_buf_v0), "NEW_EVENT event_type:%s rsc_id:%s action:%s rc:%s op_status:%s", \
109 lrmd_event_type2str(event->type), \
110 event->rsc_id, \
111 event->op_type ? event->op_type : "none", \
112 services_ocf_exitcode_str(event->rc), \
113 services_lrm_status_str(event->op_status)); \
114 crm_info("%s", event_buf_v0);
115
116 static void
test_shutdown(int nsig)117 test_shutdown(int nsig)
118 {
119 lrmd_api_delete(lrmd_conn);
120 lrmd_conn = NULL;
121 }
122
123 static void
read_events(lrmd_event_data_t * event)124 read_events(lrmd_event_data_t * event)
125 {
126 report_event(event);
127 if (options.listen) {
128 if (safe_str_eq(options.listen, event_buf_v0)) {
129 print_result(printf("LISTEN EVENT SUCCESSFUL\n"));
130 test_exit(0);
131 }
132 }
133
134 if (exec_call_id && (event->call_id == exec_call_id)) {
135 if (event->op_status == 0 && event->rc == 0) {
136 print_result(printf("API-CALL SUCCESSFUL for 'exec'\n"));
137 } else {
138 print_result(printf("API-CALL FAILURE for 'exec', rc:%d lrmd_op_status:%s\n",
139 event->rc, services_lrm_status_str(event->op_status)));
140 test_exit(-1);
141 }
142
143 if (!options.listen) {
144 test_exit(0);
145 }
146 }
147 }
148
149 static gboolean
timeout_err(gpointer data)150 timeout_err(gpointer data)
151 {
152 print_result(printf("LISTEN EVENT FAILURE - timeout occurred, never found.\n"));
153 test_exit(-1);
154
155 return FALSE;
156 }
157
158 static void
connection_events(lrmd_event_data_t * event)159 connection_events(lrmd_event_data_t * event)
160 {
161 int rc = event->connection_rc;
162
163 if (event->type != lrmd_event_connect) {
164 /* ignore */
165 return;
166 }
167
168 if (!rc) {
169 crm_info("lrmd client connection established");
170 start_test(NULL);
171 return;
172 } else {
173 sleep(1);
174 try_connect();
175 crm_notice("lrmd client connection failed");
176 }
177 }
178
179 static void
try_connect(void)180 try_connect(void)
181 {
182 int tries = 10;
183 static int num_tries = 0;
184 int rc = 0;
185
186 lrmd_conn->cmds->set_callback(lrmd_conn, connection_events);
187 for (; num_tries < tries; num_tries++) {
188 rc = lrmd_conn->cmds->connect_async(lrmd_conn, "lrmd", 3000);
189
190 if (!rc) {
191 return; /* we'll hear back in async callback */
192 }
193 sleep(1);
194 }
195
196 print_result(printf("API CONNECTION FAILURE\n"));
197 test_exit(-1);
198 }
199
200 static gboolean
start_test(gpointer user_data)201 start_test(gpointer user_data)
202 {
203 int rc = 0;
204
205 if (!options.no_connect) {
206 if (!lrmd_conn->cmds->is_connected(lrmd_conn)) {
207 try_connect();
208 /* async connect -- this function will get called back into */
209 return 0;
210 }
211 }
212 lrmd_conn->cmds->set_callback(lrmd_conn, read_events);
213
214 if (options.timeout) {
215 g_timeout_add(options.timeout, timeout_err, NULL);
216 }
217
218 if (!options.api_call) {
219 return 0;
220 }
221
222 if (safe_str_eq(options.api_call, "exec")) {
223 rc = lrmd_conn->cmds->exec(lrmd_conn,
224 options.rsc_id,
225 options.action,
226 NULL,
227 options.interval,
228 options.timeout,
229 options.start_delay, exec_call_opts, options.params);
230
231 if (rc > 0) {
232 exec_call_id = rc;
233 print_result(printf("API-CALL 'exec' action pending, waiting on response\n"));
234 }
235
236 } else if (safe_str_eq(options.api_call, "register_rsc")) {
237 rc = lrmd_conn->cmds->register_rsc(lrmd_conn,
238 options.rsc_id,
239 options.class, options.provider, options.type, 0);
240 } else if (safe_str_eq(options.api_call, "get_rsc_info")) {
241 lrmd_rsc_info_t *rsc_info;
242
243 rsc_info = lrmd_conn->cmds->get_rsc_info(lrmd_conn, options.rsc_id, 0);
244
245 if (rsc_info) {
246 print_result(printf("RSC_INFO: id:%s class:%s provider:%s type:%s\n",
247 rsc_info->id, rsc_info->class,
248 rsc_info->provider ? rsc_info->provider : "<none>",
249 rsc_info->type));
250 lrmd_free_rsc_info(rsc_info);
251 rc = pcmk_ok;
252 } else {
253 rc = -1;
254 }
255 } else if (safe_str_eq(options.api_call, "unregister_rsc")) {
256 rc = lrmd_conn->cmds->unregister_rsc(lrmd_conn, options.rsc_id, 0);
257 } else if (safe_str_eq(options.api_call, "cancel")) {
258 rc = lrmd_conn->cmds->cancel(lrmd_conn, options.rsc_id, options.action, options.interval);
259 } else if (safe_str_eq(options.api_call, "metadata")) {
260 char *output = NULL;
261
262 rc = lrmd_conn->cmds->get_metadata(lrmd_conn,
263 options.class,
264 options.provider, options.type, &output, 0);
265 if (rc == pcmk_ok) {
266 print_result(printf("%s", output));
267 free(output);
268 }
269 } else if (safe_str_eq(options.api_call, "list_agents")) {
270 lrmd_list_t *list = NULL;
271 lrmd_list_t *iter = NULL;
272
273 rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, options.class, options.provider);
274
275 if (rc > 0) {
276 print_result(printf("%d agents found\n", rc));
277 for (iter = list; iter != NULL; iter = iter->next) {
278 print_result(printf("%s\n", iter->val));
279 }
280 lrmd_list_freeall(list);
281 rc = 0;
282 } else {
283 print_result(printf("API_CALL FAILURE - no agents found\n"));
284 rc = -1;
285 }
286 } else if (safe_str_eq(options.api_call, "list_ocf_providers")) {
287 lrmd_list_t *list = NULL;
288 lrmd_list_t *iter = NULL;
289
290 rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, options.type, &list);
291
292 if (rc > 0) {
293 print_result(printf("%d providers found\n", rc));
294 for (iter = list; iter != NULL; iter = iter->next) {
295 print_result(printf("%s\n", iter->val));
296 }
297 lrmd_list_freeall(list);
298 rc = 0;
299 } else {
300 print_result(printf("API_CALL FAILURE - no providers found\n"));
301 rc = -1;
302 }
303
304 } else if (safe_str_eq(options.api_call, "list_standards")) {
305 lrmd_list_t *list = NULL;
306 lrmd_list_t *iter = NULL;
307
308 rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list);
309
310 if (rc > 0) {
311 print_result(printf("%d standards found\n", rc));
312 for (iter = list; iter != NULL; iter = iter->next) {
313 print_result(printf("%s\n", iter->val));
314 }
315 lrmd_list_freeall(list);
316 rc = 0;
317 } else {
318 print_result(printf("API_CALL FAILURE - no providers found\n"));
319 rc = -1;
320 }
321
322 } else if (options.api_call) {
323 print_result(printf("API-CALL FAILURE unknown action '%s'\n", options.action));
324 test_exit(-1);
325 }
326
327 if (rc < 0) {
328 print_result(printf("API-CALL FAILURE for '%s' api_rc:%d\n", options.api_call, rc));
329 test_exit(-1);
330 }
331
332 if (options.api_call && rc == pcmk_ok) {
333 print_result(printf("API-CALL SUCCESSFUL for '%s'\n", options.api_call));
334 if (!options.listen) {
335 test_exit(0);
336 }
337 }
338
339 if (options.no_wait) {
340 /* just make the call and exit regardless of anything else. */
341 test_exit(0);
342 }
343
344 return 0;
345 }
346
347 static int
generate_params(void)348 generate_params(void)
349 {
350 int rc = 0;
351 pe_working_set_t *data_set = NULL;
352 xmlNode *cib_xml_copy = NULL;
353 resource_t *rsc = NULL;
354 GHashTable *params = NULL;
355 GHashTable *meta = NULL;
356 GHashTableIter iter;
357
358 if (options.params) {
359 return 0;
360 }
361
362 data_set = pe_new_working_set();
363 if (data_set == NULL) {
364 crm_crit("Could not allocate working set");
365 return -ENOMEM;
366 }
367 set_bit(data_set->flags, pe_flag_no_counts);
368 set_bit(data_set->flags, pe_flag_no_compat);
369
370 cib_conn = cib_new();
371 rc = cib_conn->cmds->signon(cib_conn, "lrmd_test", cib_query);
372 if (rc != pcmk_ok) {
373 crm_err("Error signing on to the CIB service: %s", pcmk_strerror(rc));
374 rc = -1;
375 goto param_gen_bail;
376 }
377
378 rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call);
379 if (rc != pcmk_ok) {
380 crm_err("Error retrieving cib copy: %s (%d)", pcmk_strerror(rc), rc);
381 goto param_gen_bail;
382
383 } else if (cib_xml_copy == NULL) {
384 rc = -ENODATA;
385 crm_err("Error retrieving cib copy: %s (%d)", pcmk_strerror(rc), rc);
386 goto param_gen_bail;
387 }
388
389 if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) {
390 crm_err("Error updating cib configuration");
391 rc = -1;
392 goto param_gen_bail;
393 }
394
395 data_set->input = cib_xml_copy;
396 data_set->now = crm_time_new(NULL);
397
398 cluster_status(data_set);
399 if (options.rsc_id) {
400 rsc = pe_find_resource_with_flags(data_set->resources, options.rsc_id,
401 pe_find_renamed|pe_find_any);
402 }
403
404 if (!rsc) {
405 crm_err("Resource does not exist in config");
406 rc = -1;
407 goto param_gen_bail;
408 }
409
410 params = crm_str_table_new();
411 meta = crm_str_table_new();
412
413 get_rsc_attributes(params, rsc, NULL, data_set);
414 get_meta_attributes(meta, rsc, NULL, data_set);
415
416 if (params) {
417 char *key = NULL;
418 char *value = NULL;
419
420 g_hash_table_iter_init(&iter, params);
421 while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
422 options.params = lrmd_key_value_add(options.params, key, value);
423 }
424 g_hash_table_destroy(params);
425 }
426
427 if (meta) {
428 char *key = NULL;
429 char *value = NULL;
430
431 g_hash_table_iter_init(&iter, meta);
432 while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) {
433 char *crm_name = crm_meta_name(key);
434
435 options.params = lrmd_key_value_add(options.params, crm_name, value);
436 free(crm_name);
437 }
438 g_hash_table_destroy(meta);
439 }
440
441 param_gen_bail:
442 pe_free_working_set(data_set);
443 return rc;
444 }
445
446 int
main(int argc,char ** argv)447 main(int argc, char **argv)
448 {
449 int option_index = 0;
450 int argerr = 0;
451 int flag;
452 char *key = NULL;
453 char *val = NULL;
454 gboolean use_tls = FALSE;
455 crm_trigger_t *trig;
456
457 crm_set_options(NULL, "mode [options]", long_options,
458 "Inject commands into the lrmd and watch for events\n");
459
460 while (1) {
461 flag = crm_get_option(argc, argv, &option_index);
462 if (flag == -1)
463 break;
464
465 switch (flag) {
466 case '?':
467 crm_help(flag, EX_OK);
468 break;
469 case 'V':
470 options.verbose = 1;
471 break;
472 case 'Q':
473 options.quiet = 1;
474 options.verbose = 0;
475 break;
476 case 'l':
477 options.listen = optarg;
478 break;
479 case 'w':
480 options.no_wait = 1;
481 break;
482 case 'R':
483 options.is_running = 1;
484 break;
485 case 'n':
486 exec_call_opts = lrmd_opt_notify_orig_only;
487 break;
488 case 'o':
489 exec_call_opts = lrmd_opt_notify_changes_only;
490 break;
491 case 'c':
492 options.api_call = optarg;
493 break;
494 case 'a':
495 options.action = optarg;
496 break;
497 case 'r':
498 options.rsc_id = optarg;
499 break;
500 case 'x':
501 if(optarg) {
502 options.cancel_call_id = atoi(optarg);
503 }
504 break;
505 case 'P':
506 options.provider = optarg;
507 break;
508 case 'C':
509 options.class = optarg;
510 break;
511 case 'T':
512 options.type = optarg;
513 break;
514 case 'i':
515 if(optarg) {
516 options.interval = atoi(optarg);
517 }
518 break;
519 case 't':
520 if(optarg) {
521 options.timeout = atoi(optarg);
522 }
523 break;
524 case 's':
525 if(optarg) {
526 options.start_delay = atoi(optarg);
527 }
528 break;
529 case 'k':
530 key = optarg;
531 if (key && val) {
532 options.params = lrmd_key_value_add(options.params, key, val);
533 key = val = NULL;
534 }
535 break;
536 case 'v':
537 val = optarg;
538 if (key && val) {
539 options.params = lrmd_key_value_add(options.params, key, val);
540 key = val = NULL;
541 }
542 break;
543 case 'S':
544 use_tls = TRUE;
545 break;
546 default:
547 ++argerr;
548 break;
549 }
550 }
551
552 if (argerr) {
553 crm_help('?', EX_USAGE);
554 }
555 if (optind > argc) {
556 ++argerr;
557 }
558
559 if (!options.listen &&
560 (safe_str_eq(options.api_call, "metadata") ||
561 safe_str_eq(options.api_call, "list_agents") ||
562 safe_str_eq(options.api_call, "list_standards") ||
563 safe_str_eq(options.api_call, "list_ocf_providers"))) {
564 options.no_connect = 1;
565 }
566
567 crm_log_init("lrmd_ctest", LOG_INFO, TRUE, options.verbose ? TRUE : FALSE, argc, argv, FALSE);
568
569 if (options.is_running) {
570 if (!options.timeout) {
571 options.timeout = 30000;
572 }
573 options.interval = 0;
574 if (!options.rsc_id) {
575 crm_err("rsc-id must be given when is-running is used");
576 test_exit(-1);
577 }
578
579 if (generate_params()) {
580 print_result(printf
581 ("Failed to retrieve rsc parameters from cib, can not determine if rsc is running.\n"));
582 test_exit(-1);
583 }
584 options.api_call = "exec";
585 options.action = "monitor";
586 exec_call_opts = lrmd_opt_notify_orig_only;
587 }
588
589 /* if we can't perform an api_call or listen for events,
590 * there is nothing to do */
591 if (!options.api_call && !options.listen) {
592 crm_err("Nothing to be done. Please specify 'api-call' and/or 'listen'");
593 return 0;
594 }
595
596 if (use_tls) {
597 lrmd_conn = lrmd_remote_api_new(NULL, "localhost", 0);
598 } else {
599 lrmd_conn = lrmd_api_new();
600 }
601 trig = mainloop_add_trigger(G_PRIORITY_HIGH, start_test, NULL);
602 mainloop_set_trigger(trig);
603 mainloop_add_signal(SIGTERM, test_shutdown);
604
605 crm_info("Starting");
606 mainloop = g_main_new(FALSE);
607 g_main_run(mainloop);
608
609 if (cib_conn != NULL) {
610 cib_conn->cmds->signoff(cib_conn);
611 cib_delete(cib_conn);
612 }
613
614 test_exit(0);
615 return 0;
616 }
617