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. build_arg_context
  29. main

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

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