root/daemons/execd/cts-exec-helper.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. test_exit
  2. test_shutdown
  3. read_events
  4. timeout_err
  5. connection_events
  6. try_connect
  7. start_test
  8. generate_params
  9. main

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

/* [previous][next][first][last][top][bottom][index][help] */