root/lib/pacemaker/pcmk_simulate.c

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

DEFINITIONS

This source file includes following definitions.
  1. create_action_name
  2. print_cluster_status
  3. print_transition_summary
  4. reset
  5. pcmk__write_sim_dotfile
  6. pcmk__profile_file
  7. pcmk__profile_dir
  8. pcmk__set_effective_date
  9. pcmk__simulate
  10. pcmk_simulate

   1 /*
   2  * Copyright 2021 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU Lesser General Public License
   7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 #include <crm/cib/internal.h>
  12 #include <crm/common/output.h>
  13 #include <crm/common/results.h>
  14 #include <crm/pengine/pe_types.h>
  15 #include <pacemaker-internal.h>
  16 #include <pacemaker.h>
  17 
  18 #include <sys/types.h>
  19 #include <sys/stat.h>
  20 #include <unistd.h>
  21 
  22 static char *
  23 create_action_name(pe_action_t *action, bool verbose)
     /* [previous][next][first][last][top][bottom][index][help] */
  24 {
  25     char *action_name = NULL;
  26     const char *prefix = "";
  27     const char *action_host = NULL;
  28     const char *clone_name = NULL;
  29     const char *task = action->task;
  30 
  31     if (action->node) {
  32         action_host = action->node->details->uname;
  33     } else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
  34         action_host = "<none>";
  35     }
  36 
  37     if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_casei)) {
  38         prefix = "Cancel ";
  39         task = action->cancel_task;
  40     }
  41 
  42     if (action->rsc && action->rsc->clone_name) {
  43         clone_name = action->rsc->clone_name;
  44     }
  45 
  46     if (clone_name) {
  47         char *key = NULL;
  48         guint interval_ms = 0;
  49 
  50         if (pcmk__guint_from_hash(action->meta,
  51                                   XML_LRM_ATTR_INTERVAL_MS, 0,
  52                                   &interval_ms) != pcmk_rc_ok) {
  53             interval_ms = 0;
  54         }
  55 
  56         if (pcmk__strcase_any_of(action->task, RSC_NOTIFY, RSC_NOTIFIED, NULL)) {
  57             const char *n_type = g_hash_table_lookup(action->meta, "notify_key_type");
  58             const char *n_task = g_hash_table_lookup(action->meta, "notify_key_operation");
  59 
  60             CRM_ASSERT(n_type != NULL);
  61             CRM_ASSERT(n_task != NULL);
  62             key = pcmk__notify_key(clone_name, n_type, n_task);
  63 
  64         } else {
  65             key = pcmk__op_key(clone_name, task, interval_ms);
  66         }
  67 
  68         if (action_host) {
  69             action_name = crm_strdup_printf("%s%s %s", prefix, key, action_host);
  70         } else {
  71             action_name = crm_strdup_printf("%s%s", prefix, key);
  72         }
  73         free(key);
  74 
  75     } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
  76         const char *op = g_hash_table_lookup(action->meta, "stonith_action");
  77 
  78         action_name = crm_strdup_printf("%s%s '%s' %s", prefix, action->task, op, action_host);
  79 
  80     } else if (action->rsc && action_host) {
  81         action_name = crm_strdup_printf("%s%s %s", prefix, action->uuid, action_host);
  82 
  83     } else if (action_host) {
  84         action_name = crm_strdup_printf("%s%s %s", prefix, action->task, action_host);
  85 
  86     } else {
  87         action_name = crm_strdup_printf("%s", action->uuid);
  88     }
  89 
  90     if (verbose) {
  91         char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
  92 
  93         free(action_name);
  94         action_name = with_id;
  95     }
  96     return action_name;
  97 }
  98 
  99 static void
 100 print_cluster_status(pe_working_set_t * data_set, unsigned int show_opts,
     /* [previous][next][first][last][top][bottom][index][help] */
 101                      unsigned int section_opts, const char *title, bool print_spacer)
 102 {
 103     pcmk__output_t *out = data_set->priv;
 104     GList *all = NULL;
 105 
 106     section_opts |= pcmk_section_nodes | pcmk_section_resources;
 107 
 108     all = g_list_prepend(all, (gpointer) "*");
 109 
 110     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
 111     out->begin_list(out, NULL, NULL, "%s", title);
 112     out->message(out, "cluster-status", data_set, 0, NULL, FALSE,
 113                  section_opts,
 114                  show_opts | pcmk_show_inactive_rscs | pcmk_show_failed_detail,
 115                  NULL, all, all);
 116     out->end_list(out);
 117 
 118     g_list_free(all);
 119 }
 120 
 121 static void
 122 print_transition_summary(pe_working_set_t *data_set, bool print_spacer)
     /* [previous][next][first][last][top][bottom][index][help] */
 123 {
 124     pcmk__output_t *out = data_set->priv;
 125 
 126     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
 127     out->begin_list(out, NULL, NULL, "Transition Summary");
 128     LogNodeActions(data_set);
 129     g_list_foreach(data_set->resources, (GFunc) LogActions, data_set);
 130     out->end_list(out);
 131 }
 132 
 133 static void
 134 reset(pe_working_set_t *data_set, xmlNodePtr input, pcmk__output_t *out,
     /* [previous][next][first][last][top][bottom][index][help] */
 135       char *use_date, unsigned int flags)
 136 {
 137     data_set->input = input;
 138     data_set->priv = out;
 139     pcmk__set_effective_date(data_set, true, use_date);
 140     if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
 141         pe__set_working_set_flags(data_set, pe_flag_sanitized);
 142     }
 143     if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
 144         pe__set_working_set_flags(data_set, pe_flag_show_scores);
 145     }
 146     if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
 147         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 148     }
 149 }
 150 
 151 int
 152 pcmk__write_sim_dotfile(pe_working_set_t *data_set, const char *dot_file,
     /* [previous][next][first][last][top][bottom][index][help] */
 153                         bool all_actions, bool verbose)
 154 {
 155     GList *gIter = NULL;
 156     FILE *dot_strm = fopen(dot_file, "w");
 157 
 158     if (dot_strm == NULL) {
 159         return errno;
 160     }
 161 
 162     fprintf(dot_strm, " digraph \"g\" {\n");
 163     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 164         pe_action_t *action = (pe_action_t *) gIter->data;
 165         const char *style = "dashed";
 166         const char *font = "black";
 167         const char *color = "black";
 168         char *action_name = create_action_name(action, verbose);
 169 
 170         if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 171             font = "orange";
 172         }
 173 
 174         if (pcmk_is_set(action->flags, pe_action_dumped)) {
 175             style = "bold";
 176             color = "green";
 177 
 178         } else if ((action->rsc != NULL)
 179                    && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
 180             color = "red";
 181             font = "purple";
 182             if (!all_actions) {
 183                 goto do_not_write;
 184             }
 185 
 186         } else if (pcmk_is_set(action->flags, pe_action_optional)) {
 187             color = "blue";
 188             if (!all_actions) {
 189                 goto do_not_write;
 190             }
 191 
 192         } else {
 193             color = "red";
 194             CRM_CHECK(!pcmk_is_set(action->flags, pe_action_runnable), ;);
 195         }
 196 
 197         pe__set_action_flags(action, pe_action_dumped);
 198         fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
 199                 action_name, style, color, font);
 200   do_not_write:
 201         free(action_name);
 202     }
 203 
 204     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 205         pe_action_t *action = (pe_action_t *) gIter->data;
 206 
 207         GList *gIter2 = NULL;
 208 
 209         for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
 210             pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
 211 
 212             char *before_name = NULL;
 213             char *after_name = NULL;
 214             const char *style = "dashed";
 215             bool optional = true;
 216 
 217             if (before->state == pe_link_dumped) {
 218                 optional = false;
 219                 style = "bold";
 220             } else if (pcmk_is_set(action->flags, pe_action_pseudo)
 221                        && (before->type & pe_order_stonith_stop)) {
 222                 continue;
 223             } else if (before->type == pe_order_none) {
 224                 continue;
 225             } else if (pcmk_is_set(before->action->flags, pe_action_dumped)
 226                        && pcmk_is_set(action->flags, pe_action_dumped)
 227                        && before->type != pe_order_load) {
 228                 optional = false;
 229             }
 230 
 231             if (all_actions || !optional) {
 232                 before_name = create_action_name(before->action, verbose);
 233                 after_name = create_action_name(action, verbose);
 234                 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
 235                         before_name, after_name, style);
 236                 free(before_name);
 237                 free(after_name);
 238             }
 239         }
 240     }
 241 
 242     fprintf(dot_strm, "}\n");
 243     fflush(dot_strm);
 244     fclose(dot_strm);
 245     return pcmk_rc_ok;
 246 }
 247 
 248 void
 249 pcmk__profile_file(const char *xml_file, long long repeat, pe_working_set_t *data_set, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 250 {
 251     pcmk__output_t *out = data_set->priv;
 252     xmlNode *cib_object = NULL;
 253     clock_t start = 0;
 254     clock_t end;
 255 
 256     CRM_ASSERT(out != NULL);
 257 
 258     cib_object = filename2xml(xml_file);
 259     start = clock();
 260 
 261     if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
 262         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 263     }
 264 
 265     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 266         free_xml(cib_object);
 267         return;
 268     }
 269 
 270     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
 271         free_xml(cib_object);
 272         return;
 273     }
 274 
 275     for (int i = 0; i < repeat; ++i) {
 276         xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
 277 
 278         data_set->input = input;
 279         pcmk__set_effective_date(data_set, false, use_date);
 280         pcmk__schedule_actions(data_set, input, NULL);
 281         pe_reset_working_set(data_set);
 282     }
 283 
 284     end = clock();
 285     out->message(out, "profile", xml_file, start, end);
 286 }
 287 
 288 void
 289 pcmk__profile_dir(const char *dir, long long repeat, pe_working_set_t *data_set, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 290 {
 291     pcmk__output_t *out = data_set->priv;
 292     struct dirent **namelist;
 293 
 294     int file_num = scandir(dir, &namelist, 0, alphasort);
 295 
 296     CRM_ASSERT(out != NULL);
 297 
 298     if (file_num > 0) {
 299         struct stat prop;
 300         char buffer[FILENAME_MAX];
 301 
 302         out->begin_list(out, NULL, NULL, "Timings");
 303 
 304         while (file_num--) {
 305             if ('.' == namelist[file_num]->d_name[0]) {
 306                 free(namelist[file_num]);
 307                 continue;
 308 
 309             } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
 310                                             ".xml")) {
 311                 free(namelist[file_num]);
 312                 continue;
 313             }
 314             snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
 315             if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
 316                 pcmk__profile_file(buffer, repeat, data_set, use_date);
 317             }
 318             free(namelist[file_num]);
 319         }
 320         free(namelist);
 321 
 322         out->end_list(out);
 323     }
 324 }
 325 
 326 void
 327 pcmk__set_effective_date(pe_working_set_t *data_set, bool print_original, char *use_date)
     /* [previous][next][first][last][top][bottom][index][help] */
 328 {
 329     pcmk__output_t *out = data_set->priv;
 330     time_t original_date = 0;
 331 
 332     CRM_ASSERT(out != NULL);
 333 
 334     crm_element_value_epoch(data_set->input, "execution-date", &original_date);
 335 
 336     if (use_date) {
 337         data_set->now = crm_time_new(use_date);
 338         out->info(out, "Setting effective cluster time: %s", use_date);
 339         crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
 340                      crm_time_log_date | crm_time_log_timeofday);
 341 
 342     } else if (original_date) {
 343 
 344         data_set->now = crm_time_new(NULL);
 345         crm_time_set_timet(data_set->now, &original_date);
 346 
 347         if (print_original) {
 348             char *when = crm_time_as_string(data_set->now,
 349                             crm_time_log_date|crm_time_log_timeofday);
 350 
 351             out->info(out, "Using the original execution date of: %s", when);
 352             free(when);
 353         }
 354     }
 355 }
 356 
 357 int
 358 pcmk__simulate(pe_working_set_t *data_set, pcmk__output_t *out, pcmk_injections_t *injections,
     /* [previous][next][first][last][top][bottom][index][help] */
 359                unsigned int flags, unsigned int section_opts, char *use_date,
 360                char *input_file, char *graph_file, char *dot_file)
 361 {
 362     int printed = pcmk_rc_no_output;
 363     int rc = pcmk_rc_ok;
 364     xmlNodePtr input = NULL;
 365     cib_t *cib = NULL;
 366     bool modified = false;
 367 
 368     rc = cib__signon_query(&cib, &input);
 369     if (rc != pcmk_rc_ok) {
 370         goto simulate_done;
 371     }
 372 
 373     reset(data_set, input, out, use_date, flags);
 374     cluster_status(data_set);
 375 
 376     if (!out->is_quiet(out)) {
 377         if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 378             printed = out->message(out, "maint-mode", data_set->flags);
 379         }
 380 
 381         if (data_set->disabled_resources || data_set->blocked_resources) {
 382             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 383             printed = out->info(out, "%d of %d resource instances DISABLED and %d BLOCKED "
 384                                 "from further action due to failure",
 385                                 data_set->disabled_resources, data_set->ninstances,
 386                                 data_set->blocked_resources);
 387         }
 388 
 389         /* Most formatted output headers use caps for each word, but this one
 390          * only has the first word capitalized for compatibility with pcs.
 391          */
 392         print_cluster_status(data_set, pcmk_is_set(flags, pcmk_sim_show_pending) ? pcmk_show_pending : 0,
 393                              section_opts, "Current cluster status", printed == pcmk_rc_ok);
 394         printed = pcmk_rc_ok;
 395     }
 396 
 397     modified = injections->node_down != NULL || injections->node_fail != NULL ||
 398                injections->node_up != NULL || injections->op_inject != NULL ||
 399                injections->ticket_activate != NULL || injections->ticket_grant != NULL ||
 400                injections->ticket_revoke != NULL || injections->ticket_standby != NULL ||
 401                injections->watchdog != NULL || injections->watchdog != NULL;
 402 
 403     if (modified) {
 404         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 405         modify_configuration(data_set, cib, injections);
 406         printed = pcmk_rc_ok;
 407 
 408         rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
 409         if (rc != pcmk_rc_ok) {
 410             rc = pcmk_legacy2rc(rc);
 411             goto simulate_done;
 412         }
 413 
 414         cleanup_calculations(data_set);
 415         reset(data_set, input, out, use_date, flags);
 416         cluster_status(data_set);
 417     }
 418 
 419     if (input_file != NULL) {
 420         rc = write_xml_file(input, input_file, FALSE);
 421         if (rc < 0) {
 422             rc = pcmk_legacy2rc(rc);
 423             goto simulate_done;
 424         }
 425     }
 426 
 427     if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
 428         crm_time_t *local_date = NULL;
 429         pcmk__output_t *logger_out = NULL;
 430 
 431         if (pcmk_all_flags_set(data_set->flags, pe_flag_show_scores|pe_flag_show_utilization)) {
 432             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 433             out->begin_list(out, NULL, NULL, "Allocation Scores and Utilization Information");
 434             printed = pcmk_rc_ok;
 435         } else if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
 436             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 437             out->begin_list(out, NULL, NULL, "Allocation Scores");
 438             printed = pcmk_rc_ok;
 439         } else if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
 440             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 441             out->begin_list(out, NULL, NULL, "Utilization Information");
 442             printed = pcmk_rc_ok;
 443         } else {
 444             logger_out = pcmk__new_logger();
 445             if (logger_out == NULL) {
 446                 rc = pcmk_rc_error;
 447                 goto simulate_done;
 448             }
 449 
 450             data_set->priv = logger_out;
 451         }
 452 
 453         pcmk__schedule_actions(data_set, input, local_date);
 454 
 455         if (logger_out == NULL) {
 456             out->end_list(out);
 457         } else {
 458             logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
 459             pcmk__output_free(logger_out);
 460             data_set->priv = out;
 461         }
 462 
 463         input = NULL;           /* Don't try and free it twice */
 464 
 465         if (graph_file != NULL) {
 466             rc = write_xml_file(data_set->graph, graph_file, FALSE);
 467             if (rc < 0) {
 468                 rc = pcmk_rc_graph_error;
 469                 goto simulate_done;
 470             }
 471         }
 472 
 473         if (dot_file != NULL) {
 474             rc = pcmk__write_sim_dotfile(data_set, dot_file,
 475                                          pcmk_is_set(flags, pcmk_sim_all_actions),
 476                                          pcmk_is_set(flags, pcmk_sim_verbose));
 477             if (rc != pcmk_rc_ok) {
 478                 rc = pcmk_rc_dot_error;
 479                 goto simulate_done;
 480             }
 481         }
 482 
 483         if (!out->is_quiet(out)) {
 484             print_transition_summary(data_set, printed == pcmk_rc_ok);
 485         }
 486     }
 487 
 488     rc = pcmk_rc_ok;
 489 
 490     if (pcmk_is_set(flags, pcmk_sim_simulate)) {
 491         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 492         if (run_simulation(data_set, cib, injections->op_fail) != transition_complete) {
 493             rc = pcmk_rc_invalid_transition;
 494         }
 495 
 496         if (!out->is_quiet(out)) {
 497             pcmk__set_effective_date(data_set, true, use_date);
 498 
 499             if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
 500                 pe__set_working_set_flags(data_set, pe_flag_show_scores);
 501             }
 502             if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
 503                 pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 504             }
 505 
 506             cluster_status(data_set);
 507             print_cluster_status(data_set, 0, section_opts, "Revised Cluster Status", true);
 508         }
 509     }
 510 
 511 simulate_done:
 512     if (cib) {
 513         cib->cmds->signoff(cib);
 514         cib_delete(cib);
 515     }
 516 
 517     return rc;
 518 }
 519 
 520 int
 521 pcmk_simulate(xmlNodePtr *xml, pe_working_set_t *data_set, pcmk_injections_t *injections,
     /* [previous][next][first][last][top][bottom][index][help] */
 522               unsigned int flags, unsigned int section_opts, char *use_date,
 523               char *input_file, char *graph_file, char *dot_file)
 524 {
 525     pcmk__output_t *out = NULL;
 526     int rc = pcmk_rc_ok;
 527 
 528     rc = pcmk__out_prologue(&out, xml);
 529     if (rc != pcmk_rc_ok) {
 530         return rc;
 531     }
 532 
 533     pe__register_messages(out);
 534     pcmk__register_lib_messages(out);
 535 
 536     rc = pcmk__simulate(data_set, out, injections, flags, section_opts,
 537                         use_date, input_file, graph_file, dot_file);
 538     pcmk__out_epilogue(out, xml, rc);
 539     return rc;
 540 }

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