root/tools/crm_simulate.c

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

DEFINITIONS

This source file includes following definitions.
  1. in_place_cb
  2. live_check_cb
  3. node_down_cb
  4. node_fail_cb
  5. node_up_cb
  6. op_fail_cb
  7. op_inject_cb
  8. quorum_cb
  9. save_dotfile_cb
  10. save_graph_cb
  11. show_scores_cb
  12. simulate_cb
  13. ticket_activate_cb
  14. ticket_grant_cb
  15. ticket_revoke_cb
  16. ticket_standby_cb
  17. utilization_cb
  18. watchdog_cb
  19. xml_file_cb
  20. xml_pipe_cb
  21. get_date
  22. print_cluster_status
  23. create_action_name
  24. create_dotfile
  25. setup_input
  26. profile_one
  27. profile_all
  28. PCMK__OUTPUT_ARGS
  29. PCMK__OUTPUT_ARGS
  30. crm_simulate_register_messages
  31. build_arg_context
  32. main

   1 /*
   2  * Copyright 2009-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 General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdio.h>
  13 #include <unistd.h>
  14 #include <stdlib.h>
  15 #include <time.h>
  16 
  17 #include <sys/stat.h>
  18 #include <sys/param.h>
  19 #include <sys/types.h>
  20 #include <dirent.h>
  21 
  22 #include <crm/crm.h>
  23 #include <crm/cib.h>
  24 #include <crm/common/cmdline_internal.h>
  25 #include <crm/common/output_internal.h>
  26 #include <crm/common/util.h>
  27 #include <crm/common/iso8601.h>
  28 #include <crm/pengine/status.h>
  29 #include <pacemaker-internal.h>
  30 
  31 #define SUMMARY "crm_simulate - simulate a Pacemaker cluster's response to events"
  32 
  33 struct {
  34     gboolean all_actions;
  35     char *dot_file;
  36     char *graph_file;
  37     gchar *input_file;
  38     guint modified;
  39     GList *node_up;
  40     GList *node_down;
  41     GList *node_fail;
  42     GList *op_fail;
  43     GList *op_inject;
  44     gchar *output_file;
  45     gboolean print_pending;
  46     gboolean process;
  47     char *quorum;
  48     long long repeat;
  49     gboolean show_attrs;
  50     gboolean show_failcounts;
  51     gboolean show_scores;
  52     gboolean show_utilization;
  53     gboolean simulate;
  54     gboolean store;
  55     gchar *test_dir;
  56     GList *ticket_grant;
  57     GList *ticket_revoke;
  58     GList *ticket_standby;
  59     GList *ticket_activate;
  60     char *use_date;
  61     char *watchdog;
  62     char *xml_file;
  63 } options = {
  64     .print_pending = TRUE,
  65     .repeat = 1
  66 };
  67 
  68 cib_t *global_cib = NULL;
  69 bool action_numbers = FALSE;
  70 char *temp_shadow = NULL;
  71 extern gboolean bringing_nodes_online;
  72 crm_exit_t exit_code = CRM_EX_OK;
  73 
  74 #define INDENT "                                   "
  75 
  76 static pcmk__supported_format_t formats[] = {
  77     PCMK__SUPPORTED_FORMAT_NONE,
  78     PCMK__SUPPORTED_FORMAT_TEXT,
  79     PCMK__SUPPORTED_FORMAT_XML,
  80     { NULL, NULL, NULL }
  81 };
  82 
  83 static gboolean
  84 in_place_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  85     options.store = TRUE;
  86     options.process = TRUE;
  87     options.simulate = TRUE;
  88     return TRUE;
  89 }
  90 
  91 static gboolean
  92 live_check_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
  93     if (options.xml_file) {
  94         free(options.xml_file);
  95     }
  96 
  97     options.xml_file = NULL;
  98     return TRUE;
  99 }
 100 
 101 static gboolean
 102 node_down_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 103     options.modified++;
 104     options.node_down = g_list_append(options.node_down, (gchar *) g_strdup(optarg));
 105     return TRUE;
 106 }
 107 
 108 static gboolean
 109 node_fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 110     options.modified++;
 111     options.node_fail = g_list_append(options.node_fail, (gchar *) g_strdup(optarg));
 112     return TRUE;
 113 }
 114 
 115 static gboolean
 116 node_up_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 117     options.modified++;
 118     bringing_nodes_online = TRUE;
 119     options.node_up = g_list_append(options.node_up, (gchar *) g_strdup(optarg));
 120     return TRUE;
 121 }
 122 
 123 static gboolean
 124 op_fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 125     options.process = TRUE;
 126     options.simulate = TRUE;
 127     options.op_fail = g_list_append(options.op_fail, (gchar *) g_strdup(optarg));
 128     return TRUE;
 129 }
 130 
 131 static gboolean
 132 op_inject_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 133     options.modified++;
 134     options.op_inject = g_list_append(options.op_inject, (gchar *) g_strdup(optarg));
 135     return TRUE;
 136 }
 137 
 138 static gboolean
 139 quorum_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 140     if (options.quorum) {
 141         free(options.quorum);
 142     }
 143 
 144     options.modified++;
 145     options.quorum = strdup(optarg);
 146     return TRUE;
 147 }
 148 
 149 static gboolean
 150 save_dotfile_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 151     if (options.dot_file) {
 152         free(options.dot_file);
 153     }
 154 
 155     options.process = TRUE;
 156     options.dot_file = strdup(optarg);
 157     return TRUE;
 158 }
 159 
 160 static gboolean
 161 save_graph_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 162     if (options.graph_file) {
 163         free(options.graph_file);
 164     }
 165 
 166     options.process = TRUE;
 167     options.graph_file = strdup(optarg);
 168     return TRUE;
 169 }
 170 
 171 static gboolean
 172 show_scores_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 173     options.process = TRUE;
 174     options.show_scores = TRUE;
 175     return TRUE;
 176 }
 177 
 178 static gboolean
 179 simulate_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 180     options.process = TRUE;
 181     options.simulate = TRUE;
 182     return TRUE;
 183 }
 184 
 185 static gboolean
 186 ticket_activate_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 187     options.modified++;
 188     options.ticket_activate = g_list_append(options.ticket_activate, (gchar *) g_strdup(optarg));
 189     return TRUE;
 190 }
 191 
 192 static gboolean
 193 ticket_grant_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 194     options.modified++;
 195     options.ticket_grant = g_list_append(options.ticket_grant, (gchar *) g_strdup(optarg));
 196     return TRUE;
 197 }
 198 
 199 static gboolean
 200 ticket_revoke_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 201     options.modified++;
 202     options.ticket_revoke = g_list_append(options.ticket_revoke, (gchar *) g_strdup(optarg));
 203     return TRUE;
 204 }
 205 
 206 static gboolean
 207 ticket_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 208     options.modified++;
 209     options.ticket_standby = g_list_append(options.ticket_standby, (gchar *) g_strdup(optarg));
 210     return TRUE;
 211 }
 212 
 213 static gboolean
 214 utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 215     options.process = TRUE;
 216     options.show_utilization = TRUE;
 217     return TRUE;
 218 }
 219 
 220 static gboolean
 221 watchdog_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 222     if (options.watchdog) {
 223         free(options.watchdog);
 224     }
 225 
 226     options.modified++;
 227     options.watchdog = strdup(optarg);
 228     return TRUE;
 229 }
 230 
 231 static gboolean
 232 xml_file_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 233     if (options.xml_file) {
 234         free(options.xml_file);
 235     }
 236 
 237     options.xml_file = strdup(optarg);
 238     return TRUE;
 239 }
 240 
 241 static gboolean
 242 xml_pipe_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
     /* [previous][next][first][last][top][bottom][index][help] */
 243     if (options.xml_file) {
 244         free(options.xml_file);
 245     }
 246 
 247     options.xml_file = strdup("-");
 248     return TRUE;
 249 }
 250 
 251 static GOptionEntry operation_entries[] = {
 252     { "run", 'R', 0, G_OPTION_ARG_NONE, &options.process,
 253       "Process the supplied input and show what actions the cluster will take in response",
 254       NULL },
 255     { "simulate", 'S', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, simulate_cb,
 256       "Like --run, but also simulate taking those actions and show the resulting new status",
 257       NULL },
 258     { "in-place", 'X', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, in_place_cb,
 259       "Like --simulate, but also store the results back to the input file",
 260       NULL },
 261     { "show-attrs", 'A', 0, G_OPTION_ARG_NONE, &options.show_attrs,
 262       "Show node attributes",
 263       NULL },
 264     { "show-failcounts", 'c', 0, G_OPTION_ARG_NONE, &options.show_failcounts,
 265       "Show resource fail counts",
 266       NULL },
 267     { "show-scores", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_scores_cb,
 268       "Show allocation scores",
 269       NULL },
 270     { "show-utilization", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
 271       "Show utilization information",
 272       NULL },
 273     { "profile", 'P', 0, G_OPTION_ARG_FILENAME, &options.test_dir,
 274       "Process all the XML files in the named directory to create profiling data",
 275       "DIR" },
 276     { "repeat", 'N', 0, G_OPTION_ARG_INT, &options.repeat,
 277       "With --profile, repeat each test N times and print timings",
 278       "N" },
 279     /* Deprecated */
 280     { "pending", 'j', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &options.print_pending,
 281       "Display pending state if 'record-pending' is enabled",
 282       NULL },
 283 
 284     { NULL }
 285 };
 286 
 287 static GOptionEntry synthetic_entries[] = {
 288     { "node-up", 'u', 0, G_OPTION_ARG_CALLBACK, node_up_cb,
 289       "Simulate bringing a node online",
 290       "NODE" },
 291     { "node-down", 'd', 0, G_OPTION_ARG_CALLBACK, node_down_cb,
 292       "Simulate taking a node offline",
 293       "NODE" },
 294     { "node-fail", 'f', 0, G_OPTION_ARG_CALLBACK, node_fail_cb,
 295       "Simulate a node failing",
 296       "NODE" },
 297     { "op-inject", 'i', 0, G_OPTION_ARG_CALLBACK, op_inject_cb,
 298       "Generate a failure for the cluster to react to in the simulation.\n"
 299       INDENT "See `Operation Specification` help for more information.",
 300       "OPSPEC" },
 301     { "op-fail", 'F', 0, G_OPTION_ARG_CALLBACK, op_fail_cb,
 302       "If the specified task occurs during the simulation, have it fail with return code ${rc}.\n"
 303       INDENT "The transition will normally stop at the failed action.\n"
 304       INDENT "Save the result with --save-output and re-run with --xml-file.\n"
 305       INDENT "See `Operation Specification` help for more information.",
 306       "OPSPEC" },
 307     { "set-datetime", 't', 0, G_OPTION_ARG_STRING, &options.use_date,
 308       "Set date/time (ISO 8601 format, see https://en.wikipedia.org/wiki/ISO_8601)",
 309       "DATETIME" },
 310     { "quorum", 'q', 0, G_OPTION_ARG_CALLBACK, quorum_cb,
 311       "Set to '1' (or 'true') to indicate cluster has quorum",
 312       "QUORUM" },
 313     { "watchdog", 'w', 0, G_OPTION_ARG_CALLBACK, watchdog_cb,
 314       "Set to '1' (or 'true') to indicate cluster has an active watchdog device",
 315       "DEVICE" },
 316     { "ticket-grant", 'g', 0, G_OPTION_ARG_CALLBACK, ticket_grant_cb,
 317       "Simulate granting a ticket",
 318       "TICKET" },
 319     { "ticket-revoke", 'r', 0, G_OPTION_ARG_CALLBACK, ticket_revoke_cb,
 320       "Simulate revoking a ticket",
 321       "TICKET" },
 322     { "ticket-standby", 'b', 0, G_OPTION_ARG_CALLBACK, ticket_standby_cb,
 323       "Simulate making a ticket standby",
 324       "TICKET" },
 325     { "ticket-activate", 'e', 0, G_OPTION_ARG_CALLBACK, ticket_activate_cb,
 326       "Simulate activating a ticket",
 327       "TICKET" },
 328 
 329     { NULL }
 330 };
 331 
 332 static GOptionEntry artifact_entries[] = {
 333     { "save-input", 'I', 0, G_OPTION_ARG_FILENAME, &options.input_file,
 334       "Save the input configuration to the named file",
 335       "FILE" },
 336     { "save-output", 'O', 0, G_OPTION_ARG_FILENAME, &options.output_file,
 337       "Save the output configuration to the named file",
 338       "FILE" },
 339     { "save-graph", 'G', 0, G_OPTION_ARG_CALLBACK, save_graph_cb,
 340       "Save the transition graph (XML format) to the named file",
 341       "FILE" },
 342     { "save-dotfile", 'D', 0, G_OPTION_ARG_CALLBACK, save_dotfile_cb,
 343       "Save the transition graph (DOT format) to the named file",
 344       "FILE" },
 345     { "all-actions", 'a', 0, G_OPTION_ARG_NONE, &options.all_actions,
 346       "Display all possible actions in DOT graph (even if not part of transition)",
 347       NULL },
 348 
 349     { NULL }
 350 };
 351 
 352 static GOptionEntry source_entries[] = {
 353     { "live-check", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, live_check_cb,
 354       "Connect to CIB manager and use the current CIB contents as input",
 355       NULL },
 356     { "xml-file", 'x', 0, G_OPTION_ARG_CALLBACK, xml_file_cb,
 357       "Retrieve XML from the named file",
 358       "FILE" },
 359     { "xml-pipe", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, xml_pipe_cb,
 360       "Retrieve XML from stdin",
 361       NULL },
 362 
 363     { NULL }
 364 };
 365 
 366 static void
 367 get_date(pe_working_set_t *data_set, bool print_original, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 368 {
 369     pcmk__output_t *out = data_set->priv;
 370     time_t original_date = 0;
 371 
 372     crm_element_value_epoch(data_set->input, "execution-date", &original_date);
 373 
 374     if (use_date) {
 375         data_set->now = crm_time_new(use_date);
 376         out->info(out, "Setting effective cluster time: %s", use_date);
 377         crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
 378                      crm_time_log_date | crm_time_log_timeofday);
 379 
 380 
 381     } else if (original_date) {
 382 
 383         data_set->now = crm_time_new(NULL);
 384         crm_time_set_timet(data_set->now, &original_date);
 385 
 386         if (print_original) {
 387             char *when = crm_time_as_string(data_set->now,
 388                             crm_time_log_date|crm_time_log_timeofday);
 389 
 390             out->info(out, "Using the original execution date of: %s", when);
 391             free(when);
 392         }
 393     }
 394 }
 395 
 396 static void
 397 print_cluster_status(pe_working_set_t * data_set, unsigned int print_opts)
     /* [previous][next][first][last][top][bottom][index][help] */
 398 {
 399     pcmk__output_t *out = data_set->priv;
 400     int rc = pcmk_rc_no_output;
 401     GList *all = NULL;
 402 
 403     all = g_list_prepend(all, strdup("*"));
 404 
 405     rc = out->message(out, "node-list", data_set->nodes, all, all, print_opts,
 406                       FALSE, FALSE, FALSE);
 407     PCMK__OUTPUT_SPACER_IF(out, rc == pcmk_rc_ok);
 408     rc = out->message(out, "resource-list", data_set, print_opts, FALSE, TRUE,
 409                       FALSE, FALSE, all, all, FALSE);
 410 
 411     if (options.show_attrs) {
 412         out->message(out, "node-attribute-list", data_set,
 413                      0, rc == pcmk_rc_ok, FALSE, FALSE, FALSE, all, all);
 414     }
 415 
 416     if (options.show_failcounts) {
 417         out->message(out, "failed-action-list", data_set, all, all,
 418                      rc == pcmk_rc_ok);
 419     }
 420 
 421     g_list_free_full(all, free);
 422 }
 423 
 424 static char *
 425 create_action_name(pe_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 426 {
 427     char *action_name = NULL;
 428     const char *prefix = "";
 429     const char *action_host = NULL;
 430     const char *clone_name = NULL;
 431     const char *task = action->task;
 432 
 433     if (action->node) {
 434         action_host = action->node->details->uname;
 435     } else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
 436         action_host = "<none>";
 437     }
 438 
 439     if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_casei)) {
 440         prefix = "Cancel ";
 441         task = action->cancel_task;
 442     }
 443 
 444     if (action->rsc && action->rsc->clone_name) {
 445         clone_name = action->rsc->clone_name;
 446     }
 447 
 448     if (clone_name) {
 449         char *key = NULL;
 450         guint interval_ms = 0;
 451 
 452         if (pcmk__guint_from_hash(action->meta,
 453                                   XML_LRM_ATTR_INTERVAL_MS, 0,
 454                                   &interval_ms) != pcmk_rc_ok) {
 455             interval_ms = 0;
 456         }
 457 
 458         if (pcmk__strcase_any_of(action->task, RSC_NOTIFY, RSC_NOTIFIED, NULL)) {
 459             const char *n_type = g_hash_table_lookup(action->meta, "notify_key_type");
 460             const char *n_task = g_hash_table_lookup(action->meta, "notify_key_operation");
 461 
 462             CRM_ASSERT(n_type != NULL);
 463             CRM_ASSERT(n_task != NULL);
 464             key = pcmk__notify_key(clone_name, n_type, n_task);
 465 
 466         } else {
 467             key = pcmk__op_key(clone_name, task, interval_ms);
 468         }
 469 
 470         if (action_host) {
 471             action_name = crm_strdup_printf("%s%s %s", prefix, key, action_host);
 472         } else {
 473             action_name = crm_strdup_printf("%s%s", prefix, key);
 474         }
 475         free(key);
 476 
 477     } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
 478         const char *op = g_hash_table_lookup(action->meta, "stonith_action");
 479 
 480         action_name = crm_strdup_printf("%s%s '%s' %s", prefix, action->task, op, action_host);
 481 
 482     } else if (action->rsc && action_host) {
 483         action_name = crm_strdup_printf("%s%s %s", prefix, action->uuid, action_host);
 484 
 485     } else if (action_host) {
 486         action_name = crm_strdup_printf("%s%s %s", prefix, action->task, action_host);
 487 
 488     } else {
 489         action_name = crm_strdup_printf("%s", action->uuid);
 490     }
 491 
 492     if (action_numbers) { // i.e. verbose
 493         char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
 494 
 495         free(action_name);
 496         action_name = with_id;
 497     }
 498     return action_name;
 499 }
 500 
 501 static bool
 502 create_dotfile(pe_working_set_t * data_set, const char *dot_file, gboolean all_actions,
     /* [previous][next][first][last][top][bottom][index][help] */
 503                GError **error)
 504 {
 505     GList *gIter = NULL;
 506     FILE *dot_strm = fopen(dot_file, "w");
 507 
 508     if (dot_strm == NULL) {
 509         g_set_error(error, PCMK__RC_ERROR, errno,
 510                     "Could not open %s for writing: %s", dot_file,
 511                     pcmk_rc_str(errno));
 512         return false;
 513     }
 514 
 515     fprintf(dot_strm, " digraph \"g\" {\n");
 516     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 517         pe_action_t *action = (pe_action_t *) gIter->data;
 518         const char *style = "dashed";
 519         const char *font = "black";
 520         const char *color = "black";
 521         char *action_name = create_action_name(action);
 522 
 523         crm_trace("Action %d: %s %s %p", action->id, action_name, action->uuid, action);
 524 
 525         if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 526             font = "orange";
 527         }
 528 
 529         if (pcmk_is_set(action->flags, pe_action_dumped)) {
 530             style = "bold";
 531             color = "green";
 532 
 533         } else if ((action->rsc != NULL)
 534                    && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
 535             color = "red";
 536             font = "purple";
 537             if (all_actions == FALSE) {
 538                 goto do_not_write;
 539             }
 540 
 541         } else if (pcmk_is_set(action->flags, pe_action_optional)) {
 542             color = "blue";
 543             if (all_actions == FALSE) {
 544                 goto do_not_write;
 545             }
 546 
 547         } else {
 548             color = "red";
 549             CRM_CHECK(!pcmk_is_set(action->flags, pe_action_runnable), ;);
 550         }
 551 
 552         pe__set_action_flags(action, pe_action_dumped);
 553         crm_trace("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]",
 554                 action_name, style, color, font);
 555         fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
 556                 action_name, style, color, font);
 557   do_not_write:
 558         free(action_name);
 559     }
 560 
 561     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 562         pe_action_t *action = (pe_action_t *) gIter->data;
 563 
 564         GList *gIter2 = NULL;
 565 
 566         for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
 567             pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
 568 
 569             char *before_name = NULL;
 570             char *after_name = NULL;
 571             const char *style = "dashed";
 572             gboolean optional = TRUE;
 573 
 574             if (before->state == pe_link_dumped) {
 575                 optional = FALSE;
 576                 style = "bold";
 577             } else if (pcmk_is_set(action->flags, pe_action_pseudo)
 578                        && (before->type & pe_order_stonith_stop)) {
 579                 continue;
 580             } else if (before->type == pe_order_none) {
 581                 continue;
 582             } else if (pcmk_is_set(before->action->flags, pe_action_dumped)
 583                        && pcmk_is_set(action->flags, pe_action_dumped)
 584                        && before->type != pe_order_load) {
 585                 optional = FALSE;
 586             }
 587 
 588             if (all_actions || optional == FALSE) {
 589                 before_name = create_action_name(before->action);
 590                 after_name = create_action_name(action);
 591                 crm_trace("\"%s\" -> \"%s\" [ style = %s]",
 592                         before_name, after_name, style);
 593                 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
 594                         before_name, after_name, style);
 595                 free(before_name);
 596                 free(after_name);
 597             }
 598         }
 599     }
 600 
 601     fprintf(dot_strm, "}\n");
 602     fflush(dot_strm);
 603     fclose(dot_strm);
 604     return true;
 605 }
 606 
 607 static int
 608 setup_input(const char *input, const char *output, GError **error)
     /* [previous][next][first][last][top][bottom][index][help] */
 609 {
 610     int rc = pcmk_rc_ok;
 611     cib_t *cib_conn = NULL;
 612     xmlNode *cib_object = NULL;
 613     char *local_output = NULL;
 614 
 615     if (input == NULL) {
 616         /* Use live CIB */
 617         cib_conn = cib_new();
 618         rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
 619         rc = pcmk_legacy2rc(rc);
 620 
 621         if (rc == pcmk_rc_ok) {
 622             rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, cib_scope_local | cib_sync_call);
 623         }
 624 
 625         cib_conn->cmds->signoff(cib_conn);
 626         cib_delete(cib_conn);
 627         cib_conn = NULL;
 628 
 629         if (rc != pcmk_rc_ok) {
 630             rc = pcmk_legacy2rc(rc);
 631             g_set_error(error, PCMK__RC_ERROR, rc,
 632                         "Live CIB query failed: %s (%d)", pcmk_rc_str(rc), rc);
 633             return rc;
 634 
 635         } else if (cib_object == NULL) {
 636             g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_NOINPUT,
 637                         "Live CIB query failed: empty result");
 638             return pcmk_rc_no_input;
 639         }
 640 
 641     } else if (pcmk__str_eq(input, "-", pcmk__str_casei)) {
 642         cib_object = filename2xml(NULL);
 643 
 644     } else {
 645         cib_object = filename2xml(input);
 646     }
 647 
 648     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
 649         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 650     }
 651 
 652     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 653         free_xml(cib_object);
 654         return pcmk_rc_transform_failed;
 655     }
 656 
 657     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
 658         free_xml(cib_object);
 659         return pcmk_rc_schema_validation;
 660     }
 661 
 662     if (output == NULL) {
 663         char *pid = pcmk__getpid_s();
 664 
 665         local_output = get_shadow_file(pid);
 666         temp_shadow = strdup(local_output);
 667         output = local_output;
 668         free(pid);
 669     }
 670 
 671     rc = write_xml_file(cib_object, output, FALSE);
 672     free_xml(cib_object);
 673     cib_object = NULL;
 674 
 675     if (rc < 0) {
 676         rc = pcmk_legacy2rc(rc);
 677         g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_CANTCREAT,
 678                     "Could not create '%s': %s", output, pcmk_rc_str(rc));
 679         return rc;
 680     } else {
 681         setenv("CIB_file", output, 1);
 682         free(local_output);
 683         return pcmk_rc_ok;
 684     }
 685 }
 686 
 687 static void
 688 profile_one(const char *xml_file, long long repeat, pe_working_set_t *data_set, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 689 {
 690     pcmk__output_t *out = data_set->priv;
 691     xmlNode *cib_object = NULL;
 692     clock_t start = 0;
 693     clock_t end;
 694 
 695     cib_object = filename2xml(xml_file);
 696     start = clock();
 697 
 698     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
 699         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 700     }
 701 
 702 
 703     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 704         free_xml(cib_object);
 705         return;
 706     }
 707 
 708     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
 709         free_xml(cib_object);
 710         return;
 711     }
 712 
 713     for (int i = 0; i < repeat; ++i) {
 714         xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
 715 
 716         data_set->input = input;
 717         get_date(data_set, false, use_date);
 718         pcmk__schedule_actions(data_set, input, NULL);
 719         pe_reset_working_set(data_set);
 720     }
 721 
 722     end = clock();
 723     out->message(out, "profile", xml_file, start, end);
 724 }
 725 
 726 #ifndef FILENAME_MAX
 727 #  define FILENAME_MAX 512
 728 #endif
 729 
 730 static void
 731 profile_all(const char *dir, long long repeat, pe_working_set_t *data_set, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 732 {
 733     pcmk__output_t *out = data_set->priv;
 734     struct dirent **namelist;
 735 
 736     int file_num = scandir(dir, &namelist, 0, alphasort);
 737 
 738     if (file_num > 0) {
 739         struct stat prop;
 740         char buffer[FILENAME_MAX];
 741 
 742         out->begin_list(out, NULL, NULL, "Timings");
 743 
 744         while (file_num--) {
 745             if ('.' == namelist[file_num]->d_name[0]) {
 746                 free(namelist[file_num]);
 747                 continue;
 748 
 749             } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
 750                                             ".xml")) {
 751                 free(namelist[file_num]);
 752                 continue;
 753             }
 754             snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
 755             if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
 756                 profile_one(buffer, repeat, data_set, use_date);
 757             }
 758             free(namelist[file_num]);
 759         }
 760         free(namelist);
 761 
 762         out->end_list(out);
 763     }
 764 }
 765 
 766 PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 767 static int
 768 profile_default(pcmk__output_t *out, va_list args) {
 769     const char *xml_file = va_arg(args, const char *);
 770     clock_t start = va_arg(args, clock_t);
 771     clock_t end = va_arg(args, clock_t);
 772 
 773     out->list_item(out, NULL, "Testing %s ... %.2f secs", xml_file,
 774                    (end - start) / (float) CLOCKS_PER_SEC);
 775 
 776     return pcmk_rc_ok;
 777 }
 778 
 779 PCMK__OUTPUT_ARGS("profile", "const char *", "clock_t", "clock_t")
     /* [previous][next][first][last][top][bottom][index][help] */
 780 static int
 781 profile_xml(pcmk__output_t *out, va_list args) {
 782     const char *xml_file = va_arg(args, const char *);
 783     clock_t start = va_arg(args, clock_t);
 784     clock_t end = va_arg(args, clock_t);
 785 
 786     char *duration = pcmk__ftoa((end - start) / (float) CLOCKS_PER_SEC);
 787 
 788     pcmk__output_create_xml_node(out, "timing",
 789                                  "file", xml_file,
 790                                  "duration", duration,
 791                                  NULL);
 792 
 793     free(duration);
 794     return pcmk_rc_ok;
 795 }
 796 
 797 static pcmk__message_entry_t fmt_functions[] = {
 798     { "profile", "default", profile_default, },
 799     { "profile", "xml", profile_xml },
 800 
 801     { NULL }
 802 };
 803 
 804 static void
 805 crm_simulate_register_messages(pcmk__output_t *out) {
     /* [previous][next][first][last][top][bottom][index][help] */
 806     pcmk__register_messages(out, fmt_functions);
 807 }
 808 
 809 static GOptionContext *
 810 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
     /* [previous][next][first][last][top][bottom][index][help] */
 811     GOptionContext *context = NULL;
 812 
 813     GOptionEntry extra_prog_entries[] = {
 814         { "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
 815           "Display only essential output",
 816           NULL },
 817 
 818         { NULL }
 819     };
 820 
 821     const char *description = "Operation Specification:\n\n"
 822                               "The OPSPEC in any command line option is of the form\n"
 823                               "${resource}_${task}_${interval_in_ms}@${node}=${rc}\n"
 824                               "(memcached_monitor_20000@bart.example.com=7, for example).\n"
 825                               "${rc} is an OCF return code.  For more information on these\n"
 826                               "return codes, refer to https://clusterlabs.org/pacemaker/doc/en-US/Pacemaker/2.0/html/Pacemaker_Administration/s-ocf-return-codes.html\n\n"
 827                               "Examples:\n\n"
 828                               "Pretend a recurring monitor action found memcached stopped on node\n"
 829                               "fred.example.com and, during recovery, that the memcached stop\n"
 830                               "action failed:\n\n"
 831                               "\tcrm_simulate -LS --op-inject memcached:0_monitor_20000@bart.example.com=7 "
 832                               "--op-fail memcached:0_stop_0@fred.example.com=1 --save-output /tmp/memcached-test.xml\n\n"
 833                               "Now see what the reaction to the stop failed would be:\n\n"
 834                               "\tcrm_simulate -S --xml-file /tmp/memcached-test.xml\n\n";
 835 
 836     context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
 837     pcmk__add_main_args(context, extra_prog_entries);
 838     g_option_context_set_description(context, description);
 839 
 840     pcmk__add_arg_group(context, "operations", "Operations:",
 841                         "Show operations options", operation_entries);
 842     pcmk__add_arg_group(context, "synthetic", "Synthetic Cluster Events:",
 843                         "Show synthetic cluster event options", synthetic_entries);
 844     pcmk__add_arg_group(context, "artifact", "Artifact Options:",
 845                         "Show artifact options", artifact_entries);
 846     pcmk__add_arg_group(context, "source", "Data Source:",
 847                         "Show data source options", source_entries);
 848 
 849     return context;
 850 }
 851 
 852 int
 853 main(int argc, char **argv)
     /* [previous][next][first][last][top][bottom][index][help] */
 854 {
 855     int printed = pcmk_rc_no_output;
 856     int rc = pcmk_rc_ok;
 857     pe_working_set_t *data_set = NULL;
 858     pcmk__output_t *out = NULL;
 859     xmlNode *input = NULL;
 860 
 861     GError *error = NULL;
 862 
 863     GOptionGroup *output_group = NULL;
 864     pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
 865     gchar **processed_args = pcmk__cmdline_preproc(argv, "bdefgiqrtuwxDFGINO");
 866     GOptionContext *context = build_arg_context(args, &output_group);
 867 
 868     /* This must come before g_option_context_parse_strv. */
 869     options.xml_file = strdup("-");
 870 
 871     pcmk__register_formats(output_group, formats);
 872     if (!g_option_context_parse_strv(context, &processed_args, &error)) {
 873         exit_code = CRM_EX_USAGE;
 874         goto done;
 875     }
 876 
 877     pcmk__cli_init_logging("crm_simulate", args->verbosity);
 878 
 879     rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
 880     if (rc != pcmk_rc_ok) {
 881         fprintf(stderr, "Error creating output format %s: %s\n",
 882                 args->output_ty, pcmk_rc_str(rc));
 883         exit_code = CRM_EX_ERROR;
 884         goto done;
 885     }
 886 
 887     if (pcmk__str_eq(args->output_ty, "text", pcmk__str_null_matches) &&
 888         !options.show_scores && !options.show_utilization) {
 889         pcmk__force_args(context, &error, "%s --text-fancy", g_get_prgname());
 890     } else if (pcmk__str_eq(args->output_ty, "xml", pcmk__str_none)) {
 891         pcmk__force_args(context, &error, "%s --xml-simple-list --xml-substitute", g_get_prgname());
 892     }
 893 
 894     crm_simulate_register_messages(out);
 895     pe__register_messages(out);
 896     pcmk__register_lib_messages(out);
 897 
 898     out->quiet = args->quiet;
 899 
 900     if (args->version) {
 901         out->version(out, false);
 902         goto done;
 903     }
 904 
 905     if (args->verbosity > 0) {
 906 #ifdef PCMK__COMPAT_2_0
 907         /* Redirect stderr to stdout so we can grep the output */
 908         close(STDERR_FILENO);
 909         dup2(STDOUT_FILENO, STDERR_FILENO);
 910 #endif
 911         action_numbers = TRUE;
 912     }
 913 
 914     data_set = pe_new_working_set();
 915     if (data_set == NULL) {
 916         rc = ENOMEM;
 917         g_set_error(&error, PCMK__RC_ERROR, rc, "Could not allocate working set");
 918         goto done;
 919     }
 920 
 921     if (options.show_scores) {
 922         pe__set_working_set_flags(data_set, pe_flag_show_scores);
 923     }
 924     if (options.show_utilization) {
 925         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 926     }
 927     pe__set_working_set_flags(data_set, pe_flag_no_compat);
 928 
 929     if (options.test_dir != NULL) {
 930         data_set->priv = out;
 931         profile_all(options.test_dir, options.repeat, data_set, options.use_date);
 932         rc = pcmk_rc_ok;
 933         goto done;
 934     }
 935 
 936     rc = setup_input(options.xml_file, options.store ? options.xml_file : options.output_file, &error);
 937     if (rc != pcmk_rc_ok) {
 938         goto done;
 939     }
 940 
 941     global_cib = cib_new();
 942     rc = global_cib->cmds->signon(global_cib, crm_system_name, cib_command);
 943     if (rc != pcmk_rc_ok) {
 944         rc = pcmk_legacy2rc(rc);
 945         g_set_error(&error, PCMK__RC_ERROR, rc,
 946                     "Could not connect to the CIB: %s", pcmk_rc_str(rc));
 947         goto done;
 948     }
 949 
 950     rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call | cib_scope_local);
 951     if (rc != pcmk_rc_ok) {
 952         rc = pcmk_legacy2rc(rc);
 953         g_set_error(&error, PCMK__RC_ERROR, rc,
 954                     "Could not get local CIB: %s", pcmk_rc_str(rc));
 955         goto done;
 956     }
 957 
 958     data_set->input = input;
 959     data_set->priv = out;
 960     get_date(data_set, true, options.use_date);
 961     if(options.xml_file) {
 962         pe__set_working_set_flags(data_set, pe_flag_sanitized);
 963     }
 964     if (options.show_scores) {
 965         pe__set_working_set_flags(data_set, pe_flag_show_scores);
 966     }
 967     if (options.show_utilization) {
 968         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 969     }
 970     cluster_status(data_set);
 971 
 972     if (!out->is_quiet(out)) {
 973         unsigned int opts = options.print_pending ? pe_print_pending : 0;
 974 
 975         if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 976             printed = out->message(out, "maint-mode", data_set->flags);
 977         }
 978 
 979         if (data_set->disabled_resources || data_set->blocked_resources) {
 980             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 981             printed = out->info(out, "%d of %d resource instances DISABLED and %d BLOCKED "
 982                                 "from further action due to failure",
 983                                 data_set->disabled_resources, data_set->ninstances,
 984                                 data_set->blocked_resources);
 985         }
 986 
 987         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 988         /* Most formatted output headers use caps for each word, but this one
 989          * only has the first word capitalized for compatibility with pcs.
 990          */
 991         out->begin_list(out, NULL, NULL, "Current cluster status");
 992         print_cluster_status(data_set, opts);
 993         out->end_list(out);
 994         printed = pcmk_rc_ok;
 995     }
 996 
 997     if (options.modified) {
 998         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 999         modify_configuration(data_set, global_cib, options.quorum, options.watchdog, options.node_up,
1000                              options.node_down, options.node_fail, options.op_inject,
1001                              options.ticket_grant, options.ticket_revoke, options.ticket_standby,
1002                              options.ticket_activate);
1003         printed = pcmk_rc_ok;
1004 
1005         rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call);
1006         if (rc != pcmk_rc_ok) {
1007             rc = pcmk_legacy2rc(rc);
1008             g_set_error(&error, PCMK__RC_ERROR, rc,
1009                         "Could not get modified CIB: %s", pcmk_rc_str(rc));
1010             goto done;
1011         }
1012 
1013         cleanup_calculations(data_set);
1014         data_set->input = input;
1015         data_set->priv = out;
1016         get_date(data_set, true, options.use_date);
1017 
1018         if(options.xml_file) {
1019             pe__set_working_set_flags(data_set, pe_flag_sanitized);
1020         }
1021         if (options.show_scores) {
1022             pe__set_working_set_flags(data_set, pe_flag_show_scores);
1023         }
1024         if (options.show_utilization) {
1025             pe__set_working_set_flags(data_set, pe_flag_show_utilization);
1026         }
1027         cluster_status(data_set);
1028     }
1029 
1030     if (options.input_file != NULL) {
1031         rc = write_xml_file(input, options.input_file, FALSE);
1032         if (rc < 0) {
1033             rc = pcmk_legacy2rc(rc);
1034             g_set_error(&error, PCMK__RC_ERROR, rc,
1035                         "Could not create '%s': %s", options.input_file, pcmk_rc_str(rc));
1036             goto done;
1037         }
1038     }
1039 
1040     if (options.process || options.simulate) {
1041         crm_time_t *local_date = NULL;
1042         pcmk__output_t *logger_out = NULL;
1043 
1044         if (pcmk_all_flags_set(data_set->flags, pe_flag_show_scores|pe_flag_show_utilization)) {
1045             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1046             out->begin_list(out, NULL, NULL, "Allocation Scores and Utilization Information");
1047             printed = pcmk_rc_ok;
1048         } else if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
1049             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1050             out->begin_list(out, NULL, NULL, "Allocation Scores");
1051             printed = pcmk_rc_ok;
1052         } else if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
1053             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1054             out->begin_list(out, NULL, NULL, "Utilization Information");
1055             printed = pcmk_rc_ok;
1056         } else {
1057             logger_out = pcmk__new_logger();
1058             if (logger_out == NULL) {
1059                 goto done;
1060             }
1061 
1062             data_set->priv = logger_out;
1063         }
1064 
1065         pcmk__schedule_actions(data_set, input, local_date);
1066 
1067         if (logger_out == NULL) {
1068             out->end_list(out);
1069         } else {
1070             logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
1071             pcmk__output_free(logger_out);
1072             data_set->priv = out;
1073         }
1074 
1075         input = NULL;           /* Don't try and free it twice */
1076 
1077         if (options.graph_file != NULL) {
1078             write_xml_file(data_set->graph, options.graph_file, FALSE);
1079         }
1080 
1081         if (options.dot_file != NULL) {
1082             if (!create_dotfile(data_set, options.dot_file, options.all_actions, &error)) {
1083                 goto done;
1084             }
1085         }
1086 
1087         if (!out->is_quiet(out)) {
1088             GList *gIter = NULL;
1089 
1090             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1091             out->begin_list(out, NULL, NULL, "Transition Summary");
1092 
1093             LogNodeActions(data_set);
1094             for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
1095                 pe_resource_t *rsc = (pe_resource_t *) gIter->data;
1096 
1097                 LogActions(rsc, data_set);
1098             }
1099 
1100             out->end_list(out);
1101             printed = pcmk_rc_ok;
1102         }
1103     }
1104 
1105     rc = pcmk_rc_ok;
1106 
1107     if (options.simulate) {
1108         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1109         if (run_simulation(data_set, global_cib, options.op_fail) != pcmk_rc_ok) {
1110             rc = pcmk_rc_error;
1111         }
1112 
1113         printed = pcmk_rc_ok;
1114 
1115         if (!out->is_quiet(out)) {
1116             get_date(data_set, true, options.use_date);
1117 
1118             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1119             out->begin_list(out, NULL, NULL, "Revised Cluster Status");
1120 
1121             if (options.show_scores) {
1122                 pe__set_working_set_flags(data_set, pe_flag_show_scores);
1123             }
1124             if (options.show_utilization) {
1125                 pe__set_working_set_flags(data_set, pe_flag_show_utilization);
1126             }
1127 
1128             cluster_status(data_set);
1129             print_cluster_status(data_set, 0);
1130 
1131             out->end_list(out);
1132         }
1133     }
1134 
1135   done:
1136     pcmk__output_and_clear_error(error, NULL);
1137 
1138     /* There sure is a lot to free in options. */
1139     free(options.dot_file);
1140     free(options.graph_file);
1141     g_free(options.input_file);
1142     g_list_free_full(options.node_up, g_free);
1143     g_list_free_full(options.node_down, g_free);
1144     g_list_free_full(options.node_fail, g_free);
1145     g_list_free_full(options.op_fail, g_free);
1146     g_list_free_full(options.op_inject, g_free);
1147     g_free(options.output_file);
1148     free(options.quorum);
1149     g_free(options.test_dir);
1150     g_list_free_full(options.ticket_grant, g_free);
1151     g_list_free_full(options.ticket_revoke, g_free);
1152     g_list_free_full(options.ticket_standby, g_free);
1153     g_list_free_full(options.ticket_activate, g_free);
1154     free(options.use_date);
1155     free(options.watchdog);
1156     free(options.xml_file);
1157 
1158     pcmk__free_arg_context(context);
1159     g_strfreev(processed_args);
1160 
1161     if (data_set) {
1162         pe_free_working_set(data_set);
1163     }
1164 
1165     if (global_cib) {
1166         global_cib->cmds->signoff(global_cib);
1167         cib_delete(global_cib);
1168     }
1169 
1170     fflush(stderr);
1171 
1172     if (temp_shadow) {
1173         unlink(temp_shadow);
1174         free(temp_shadow);
1175     }
1176 
1177     if (rc != pcmk_rc_ok) {
1178         exit_code = pcmk_rc2exitc(rc);
1179     }
1180 
1181     if (out != NULL) {
1182         out->finish(out, exit_code, true, NULL);
1183         pcmk__output_free(out);
1184     }
1185 
1186     crm_exit(exit_code);
1187 }

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