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