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

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