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. write_sim_dotfile
  6. profile_file
  7. pcmk__profile_dir
  8. set_effective_date
  9. simulate_pseudo_action
  10. simulate_resource_action
  11. simulate_cluster_action
  12. simulate_fencing_action
  13. pcmk__simulate_transition
  14. pcmk__simulate
  15. pcmk_simulate

   1 /*
   2  * Copyright 2021-2022 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 <stdint.h>
  19 #include <sys/types.h>
  20 #include <sys/stat.h>
  21 #include <unistd.h>
  22 
  23 #include "libpacemaker_private.h"
  24 
  25 #define STATUS_PATH_MAX 512
  26 
  27 static pcmk__output_t *out = NULL;
  28 static cib_t *fake_cib = NULL;
  29 static GList *fake_resource_list = NULL;
  30 static GList *fake_op_fail_list = NULL;
  31 
  32 static void set_effective_date(pe_working_set_t *data_set, bool print_original,
  33                                char *use_date);
  34 
  35 /*!
  36  * \internal
  37  * \brief Create an action name for use in a dot graph
  38  *
  39  * \param[in] action   Action to create name for
  40  * \param[in] verbose  If true, add action ID to name
  41  *
  42  * \return Newly allocated string with action name
  43  * \note It is the caller's responsibility to free the result.
  44  */
  45 static char *
  46 create_action_name(pe_action_t *action, bool verbose)
     /* [previous][next][first][last][top][bottom][index][help] */
  47 {
  48     char *action_name = NULL;
  49     const char *prefix = "";
  50     const char *action_host = NULL;
  51     const char *clone_name = NULL;
  52     const char *task = action->task;
  53 
  54     if (action->node != NULL) {
  55         action_host = action->node->details->uname;
  56     } else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
  57         action_host = "<none>";
  58     }
  59 
  60     if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_none)) {
  61         prefix = "Cancel ";
  62         task = action->cancel_task;
  63     }
  64 
  65     if (action->rsc != NULL) {
  66         clone_name = action->rsc->clone_name;
  67     }
  68 
  69     if (clone_name != NULL) {
  70         char *key = NULL;
  71         guint interval_ms = 0;
  72 
  73         if (pcmk__guint_from_hash(action->meta,
  74                                   XML_LRM_ATTR_INTERVAL_MS, 0,
  75                                   &interval_ms) != pcmk_rc_ok) {
  76             interval_ms = 0;
  77         }
  78 
  79         if (pcmk__strcase_any_of(action->task, RSC_NOTIFY, RSC_NOTIFIED,
  80                                  NULL)) {
  81             const char *n_type = g_hash_table_lookup(action->meta,
  82                                                      "notify_key_type");
  83             const char *n_task = g_hash_table_lookup(action->meta,
  84                                                      "notify_key_operation");
  85 
  86             CRM_ASSERT(n_type != NULL);
  87             CRM_ASSERT(n_task != NULL);
  88             key = pcmk__notify_key(clone_name, n_type, n_task);
  89         } else {
  90             key = pcmk__op_key(clone_name, task, interval_ms);
  91         }
  92 
  93         if (action_host != NULL) {
  94             action_name = crm_strdup_printf("%s%s %s",
  95                                             prefix, key, action_host);
  96         } else {
  97             action_name = crm_strdup_printf("%s%s", prefix, key);
  98         }
  99         free(key);
 100 
 101     } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
 102         const char *op = g_hash_table_lookup(action->meta, "stonith_action");
 103 
 104         action_name = crm_strdup_printf("%s%s '%s' %s",
 105                                         prefix, action->task, op, action_host);
 106 
 107     } else if (action->rsc && action_host) {
 108         action_name = crm_strdup_printf("%s%s %s",
 109                                         prefix, action->uuid, action_host);
 110 
 111     } else if (action_host) {
 112         action_name = crm_strdup_printf("%s%s %s",
 113                                         prefix, action->task, action_host);
 114 
 115     } else {
 116         action_name = crm_strdup_printf("%s", action->uuid);
 117     }
 118 
 119     if (verbose) {
 120         char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
 121 
 122         free(action_name);
 123         action_name = with_id;
 124     }
 125     return action_name;
 126 }
 127 
 128 /*!
 129  * \internal
 130  * \brief Display the status of a cluster
 131  *
 132  * \param[in] data_set      Cluster working set
 133  * \param[in] show_opts     How to modify display (as pcmk_show_opt_e flags)
 134  * \param[in] section_opts  Sections to display (as pcmk_section_e flags)
 135  * \param[in] title         What to use as list title
 136  * \param[in] print_spacer  Whether to display a spacer first
 137  */
 138 static void
 139 print_cluster_status(pe_working_set_t *data_set, uint32_t show_opts,
     /* [previous][next][first][last][top][bottom][index][help] */
 140                      uint32_t section_opts, const char *title, bool print_spacer)
 141 {
 142     pcmk__output_t *out = data_set->priv;
 143     GList *all = NULL;
 144     crm_exit_t stonith_rc = 0;
 145 
 146     section_opts |= pcmk_section_nodes | pcmk_section_resources;
 147     show_opts |= pcmk_show_inactive_rscs | pcmk_show_failed_detail;
 148 
 149     all = g_list_prepend(all, (gpointer) "*");
 150 
 151     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
 152     out->begin_list(out, NULL, NULL, "%s", title);
 153     out->message(out, "cluster-status", data_set, stonith_rc, NULL, FALSE,
 154                  section_opts, show_opts, NULL, all, all);
 155     out->end_list(out);
 156 
 157     g_list_free(all);
 158 }
 159 
 160 /*!
 161  * \internal
 162  * \brief Display a summary of all actions scheduled in a transition
 163  *
 164  * \param[in] data_set      Cluster working set (fully scheduled)
 165  * \param[in] print_spacer  Whether to display a spacer first
 166  */
 167 static void
 168 print_transition_summary(pe_working_set_t *data_set, bool print_spacer)
     /* [previous][next][first][last][top][bottom][index][help] */
 169 {
 170     pcmk__output_t *out = data_set->priv;
 171 
 172     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
 173     out->begin_list(out, NULL, NULL, "Transition Summary");
 174     pcmk__output_actions(data_set);
 175     out->end_list(out);
 176 }
 177 
 178 /*!
 179  * \internal
 180  * \brief Reset a cluster working set's input, output, date, and flags
 181  *
 182  * \param[in] data_set  Cluster working set
 183  * \param[in] input     What to set as cluster input
 184  * \param[in] out       What to set as cluster output object
 185  * \param[in] use_date  What to set as cluster's current timestamp
 186  * \param[in] flags     Cluster flags to add (pe_flag_*)
 187  */
 188 static void
 189 reset(pe_working_set_t *data_set, xmlNodePtr input, pcmk__output_t *out,
     /* [previous][next][first][last][top][bottom][index][help] */
 190       char *use_date, unsigned int flags)
 191 {
 192     data_set->input = input;
 193     data_set->priv = out;
 194     set_effective_date(data_set, true, use_date);
 195     if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
 196         pe__set_working_set_flags(data_set, pe_flag_sanitized);
 197     }
 198     if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
 199         pe__set_working_set_flags(data_set, pe_flag_show_scores);
 200     }
 201     if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
 202         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 203     }
 204 }
 205 
 206 /*!
 207  * \brief Write out a file in dot(1) format describing the actions that will
 208  *        be taken by the scheduler in response to an input CIB file.
 209  *
 210  * \param[in] data_set     Working set for the cluster
 211  * \param[in] dot_file     The filename to write
 212  * \param[in] all_actions  Write all actions, even those that are optional or
 213  *                         are on unmanaged resources
 214  * \param[in] verbose      Add extra information, such as action IDs, to the
 215  *                         output
 216  *
 217  * \return Standard Pacemaker return code
 218  */
 219 static int
 220 write_sim_dotfile(pe_working_set_t *data_set, const char *dot_file,
     /* [previous][next][first][last][top][bottom][index][help] */
 221                   bool all_actions, bool verbose)
 222 {
 223     GList *gIter = NULL;
 224     FILE *dot_strm = fopen(dot_file, "w");
 225 
 226     if (dot_strm == NULL) {
 227         return errno;
 228     }
 229 
 230     fprintf(dot_strm, " digraph \"g\" {\n");
 231     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 232         pe_action_t *action = (pe_action_t *) gIter->data;
 233         const char *style = "dashed";
 234         const char *font = "black";
 235         const char *color = "black";
 236         char *action_name = create_action_name(action, verbose);
 237 
 238         if (pcmk_is_set(action->flags, pe_action_pseudo)) {
 239             font = "orange";
 240         }
 241 
 242         if (pcmk_is_set(action->flags, pe_action_dumped)) {
 243             style = "bold";
 244             color = "green";
 245 
 246         } else if ((action->rsc != NULL)
 247                    && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
 248             color = "red";
 249             font = "purple";
 250             if (!all_actions) {
 251                 goto do_not_write;
 252             }
 253 
 254         } else if (pcmk_is_set(action->flags, pe_action_optional)) {
 255             color = "blue";
 256             if (!all_actions) {
 257                 goto do_not_write;
 258             }
 259 
 260         } else {
 261             color = "red";
 262             CRM_LOG_ASSERT(!pcmk_is_set(action->flags, pe_action_runnable));
 263         }
 264 
 265         pe__set_action_flags(action, pe_action_dumped);
 266         fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
 267                 action_name, style, color, font);
 268   do_not_write:
 269         free(action_name);
 270     }
 271 
 272     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
 273         pe_action_t *action = (pe_action_t *) gIter->data;
 274 
 275         GList *gIter2 = NULL;
 276 
 277         for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
 278             pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
 279 
 280             char *before_name = NULL;
 281             char *after_name = NULL;
 282             const char *style = "dashed";
 283             bool optional = true;
 284 
 285             if (before->state == pe_link_dumped) {
 286                 optional = false;
 287                 style = "bold";
 288             } else if (before->type == pe_order_none) {
 289                 continue;
 290             } else if (pcmk_is_set(before->action->flags, pe_action_dumped)
 291                        && pcmk_is_set(action->flags, pe_action_dumped)
 292                        && before->type != pe_order_load) {
 293                 optional = false;
 294             }
 295 
 296             if (all_actions || !optional) {
 297                 before_name = create_action_name(before->action, verbose);
 298                 after_name = create_action_name(action, verbose);
 299                 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
 300                         before_name, after_name, style);
 301                 free(before_name);
 302                 free(after_name);
 303             }
 304         }
 305     }
 306 
 307     fprintf(dot_strm, "}\n");
 308     fflush(dot_strm);
 309     fclose(dot_strm);
 310     return pcmk_rc_ok;
 311 }
 312 
 313 /*!
 314  * \brief Profile the configuration updates and scheduler actions in a single
 315  *        CIB file, printing the profiling timings.
 316  *
 317  * \note \p data_set->priv must have been set to a valid \p pcmk__output_t
 318  *       object before this function is called.
 319  *
 320  * \param[in] xml_file  The CIB file to profile
 321  * \param[in] repeat    Number of times to run
 322  * \param[in] data_set  Working set for the cluster
 323  * \param[in] use_date  The date to set the cluster's time to (may be NULL)
 324  */
 325 static void
 326 profile_file(const char *xml_file, long long repeat, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 327              char *use_date)
 328 {
 329     pcmk__output_t *out = data_set->priv;
 330     xmlNode *cib_object = NULL;
 331     clock_t start = 0;
 332     clock_t end;
 333     unsigned long long data_set_flags = pe_flag_no_compat;
 334 
 335     CRM_ASSERT(out != NULL);
 336 
 337     cib_object = filename2xml(xml_file);
 338     start = clock();
 339 
 340     if (pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS) == NULL) {
 341         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
 342     }
 343 
 344     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
 345         free_xml(cib_object);
 346         return;
 347     }
 348 
 349     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
 350         free_xml(cib_object);
 351         return;
 352     }
 353 
 354     if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
 355         data_set_flags |= pe_flag_show_scores;
 356     }
 357     if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
 358         data_set_flags |= pe_flag_show_utilization;
 359     }
 360 
 361     for (int i = 0; i < repeat; ++i) {
 362         xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
 363 
 364         data_set->input = input;
 365         set_effective_date(data_set, false, use_date);
 366         pcmk__schedule_actions(input, data_set_flags, data_set);
 367         pe_reset_working_set(data_set);
 368     }
 369 
 370     end = clock();
 371     out->message(out, "profile", xml_file, start, end);
 372 }
 373 
 374 void
 375 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] */
 376 {
 377     pcmk__output_t *out = data_set->priv;
 378     struct dirent **namelist;
 379 
 380     int file_num = scandir(dir, &namelist, 0, alphasort);
 381 
 382     CRM_ASSERT(out != NULL);
 383 
 384     if (file_num > 0) {
 385         struct stat prop;
 386         char buffer[FILENAME_MAX];
 387 
 388         out->begin_list(out, NULL, NULL, "Timings");
 389 
 390         while (file_num--) {
 391             if ('.' == namelist[file_num]->d_name[0]) {
 392                 free(namelist[file_num]);
 393                 continue;
 394 
 395             } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
 396                                             ".xml")) {
 397                 free(namelist[file_num]);
 398                 continue;
 399             }
 400             snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
 401             if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
 402                 profile_file(buffer, repeat, data_set, use_date);
 403             }
 404             free(namelist[file_num]);
 405         }
 406         free(namelist);
 407 
 408         out->end_list(out);
 409     }
 410 }
 411 
 412 /*!
 413  * \brief Set the date of the cluster, either to the value given by
 414  *        \p use_date, or to the "execution-date" value in the CIB.
 415  *
 416  * \note \p data_set->priv must have been set to a valid \p pcmk__output_t
 417  *       object before this function is called.
 418  *
 419  * \param[in,out] data_set        Working set for the cluster
 420  * \param[in]     print_original  If \p true, the "execution-date" should
 421  *                                also be printed
 422  * \param[in]     use_date        The date to set the cluster's time to
 423  *                                (may be NULL)
 424  */
 425 static void
 426 set_effective_date(pe_working_set_t *data_set, bool print_original,
     /* [previous][next][first][last][top][bottom][index][help] */
 427                    char *use_date)
 428 {
 429     pcmk__output_t *out = data_set->priv;
 430     time_t original_date = 0;
 431 
 432     CRM_ASSERT(out != NULL);
 433 
 434     crm_element_value_epoch(data_set->input, "execution-date", &original_date);
 435 
 436     if (use_date) {
 437         data_set->now = crm_time_new(use_date);
 438         out->info(out, "Setting effective cluster time: %s", use_date);
 439         crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
 440                      crm_time_log_date | crm_time_log_timeofday);
 441 
 442     } else if (original_date) {
 443 
 444         data_set->now = crm_time_new(NULL);
 445         crm_time_set_timet(data_set->now, &original_date);
 446 
 447         if (print_original) {
 448             char *when = crm_time_as_string(data_set->now,
 449                             crm_time_log_date|crm_time_log_timeofday);
 450 
 451             out->info(out, "Using the original execution date of: %s", when);
 452             free(when);
 453         }
 454     }
 455 }
 456 
 457 /*!
 458  * \internal
 459  * \brief Simulate successfully executing a pseudo-action in a graph
 460  *
 461  * \param[in] graph   Graph to update with pseudo-action result
 462  * \param[in] action  Pseudo-action to simulate executing
 463  *
 464  * \return TRUE
 465  */
 466 static gboolean
 467 simulate_pseudo_action(crm_graph_t *graph, crm_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 468 {
 469     const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
 470     const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
 471 
 472     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
 473     out->message(out, "inject-pseudo-action", node, task);
 474 
 475     pcmk__update_graph(graph, action);
 476     return TRUE;
 477 }
 478 
 479 /*!
 480  * \internal
 481  * \brief Simulate executing a resource action in a graph
 482  *
 483  * \param[in] graph   Graph to update with resource action result
 484  * \param[in] action  Resource action to simulate executing
 485  *
 486  * \return TRUE if action is validly specified, otherwise FALSE
 487  */
 488 static gboolean
 489 simulate_resource_action(crm_graph_t *graph, crm_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 490 {
 491     int rc;
 492     lrmd_event_data_t *op = NULL;
 493     int target_outcome = PCMK_OCF_OK;
 494 
 495     const char *rtype = NULL;
 496     const char *rclass = NULL;
 497     const char *resource = NULL;
 498     const char *rprovider = NULL;
 499     const char *resource_config_name = NULL;
 500     const char *operation = crm_element_value(action->xml, "operation");
 501     const char *target_rc_s = crm_meta_value(action->params,
 502                                              XML_ATTR_TE_TARGET_RC);
 503 
 504     xmlNode *cib_node = NULL;
 505     xmlNode *cib_resource = NULL;
 506     xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
 507 
 508     char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
 509     char *uuid = NULL;
 510     const char *router_node = crm_element_value(action->xml,
 511                                                 XML_LRM_ATTR_ROUTER_NODE);
 512 
 513     // Certain actions don't need to be displayed or history entries
 514     if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) {
 515         crm_debug("No history injection for %s op on %s", operation, node);
 516         goto done; // Confirm action and update graph
 517     }
 518 
 519     if (action_rsc == NULL) { // Shouldn't be possible
 520         crm_log_xml_err(action->xml, "Bad");
 521         free(node);
 522         return FALSE;
 523     }
 524 
 525     /* A resource might be known by different names in the configuration and in
 526      * the action (for example, a clone instance). Grab the configuration name
 527      * (which is preferred when writing history), and if necessary, the instance
 528      * name.
 529      */
 530     resource_config_name = crm_element_value(action_rsc, XML_ATTR_ID);
 531     if (resource_config_name == NULL) { // Shouldn't be possible
 532         crm_log_xml_err(action->xml, "No ID");
 533         free(node);
 534         return FALSE;
 535     }
 536     resource = resource_config_name;
 537     if (pe_find_resource(fake_resource_list, resource) == NULL) {
 538         const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG);
 539 
 540         if ((longname != NULL)
 541             && (pe_find_resource(fake_resource_list, longname) != NULL)) {
 542             resource = longname;
 543         }
 544     }
 545 
 546     // Certain actions need to be displayed but don't need history entries
 547     if (pcmk__strcase_any_of(operation, "delete", RSC_METADATA, NULL)) {
 548         out->message(out, "inject-rsc-action", resource, operation, node,
 549                      (guint) 0);
 550         goto done; // Confirm action and update graph
 551     }
 552 
 553     rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS);
 554     rtype = crm_element_value(action_rsc, XML_ATTR_TYPE);
 555     rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER);
 556 
 557     pcmk__scan_min_int(target_rc_s, &target_outcome, 0);
 558 
 559     CRM_ASSERT(fake_cib->cmds->query(fake_cib, NULL, NULL,
 560                                      cib_sync_call|cib_scope_local) == pcmk_ok);
 561 
 562     // Ensure the action node is in the CIB
 563     uuid = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET_UUID);
 564     cib_node = pcmk__inject_node(fake_cib, node,
 565                                  ((router_node == NULL)? uuid: node));
 566     free(uuid);
 567     CRM_ASSERT(cib_node != NULL);
 568 
 569     // Add a history entry for the action
 570     cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
 571                                                  resource_config_name,
 572                                                  rclass, rtype, rprovider);
 573     if (cib_resource == NULL) {
 574         crm_err("Could not simulate action %d history for resource %s",
 575                 action->id, resource);
 576         free(node);
 577         free_xml(cib_node);
 578         return FALSE;
 579     }
 580 
 581     // Simulate and display an executor event for the action result
 582     op = pcmk__event_from_graph_action(cib_resource, action, PCMK_EXEC_DONE,
 583                                        target_outcome, "User-injected result");
 584     out->message(out, "inject-rsc-action", resource, op->op_type, node,
 585                  op->interval_ms);
 586 
 587     // Check whether action is in a list of desired simulated failures
 588     for (GList *iter = fake_op_fail_list; iter != NULL; iter = iter->next) {
 589         char *spec = (char *) iter->data;
 590         char *key = NULL;
 591         const char *match_name = NULL;
 592 
 593         // Allow user to specify anonymous clone with or without instance number
 594         key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
 595                                 op->interval_ms, node);
 596         if (strncasecmp(key, spec, strlen(key)) == 0) {
 597             match_name = resource;
 598         }
 599         free(key);
 600 
 601         // If not found, try the resource's name in the configuration
 602         if ((match_name == NULL)
 603             && (strcmp(resource, resource_config_name) != 0)) {
 604 
 605             key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
 606                                     op->op_type, op->interval_ms, node);
 607             if (strncasecmp(key, spec, strlen(key)) == 0) {
 608                 match_name = resource_config_name;
 609             }
 610             free(key);
 611         }
 612 
 613         if (match_name == NULL) {
 614             continue; // This failed action entry doesn't match
 615         }
 616 
 617         // ${match_name}_${task}_${interval_in_ms}@${node}=${rc}
 618         rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
 619         if (rc != 1) {
 620             out->err(out, "Invalid failed operation '%s' "
 621                           "(result code must be integer)", spec);
 622             continue; // Keep checking other list entries
 623         }
 624 
 625         out->info(out, "Pretending action %d failed with rc=%d",
 626                   action->id, op->rc);
 627         crm__set_graph_action_flags(action, pcmk__graph_action_failed);
 628         graph->abort_priority = INFINITY;
 629         pcmk__inject_failcount(out, cib_node, match_name, op->op_type,
 630                                op->interval_ms, op->rc);
 631         break;
 632     }
 633 
 634     pcmk__inject_action_result(cib_resource, op, target_outcome);
 635     lrmd_free_event(op);
 636     rc = fake_cib->cmds->modify(fake_cib, XML_CIB_TAG_STATUS, cib_node,
 637                                 cib_sync_call|cib_scope_local);
 638     CRM_ASSERT(rc == pcmk_ok);
 639 
 640   done:
 641     free(node);
 642     free_xml(cib_node);
 643     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
 644     pcmk__update_graph(graph, action);
 645     return TRUE;
 646 }
 647 
 648 /*!
 649  * \internal
 650  * \brief Simulate successfully executing a cluster action
 651  *
 652  * \param[in] graph   Graph to update with action result
 653  * \param[in] action  Cluster action to simulate
 654  *
 655  * \return TRUE
 656  */
 657 static gboolean
 658 simulate_cluster_action(crm_graph_t *graph, crm_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 659 {
 660     const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
 661     const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
 662     xmlNode *rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
 663 
 664     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
 665     out->message(out, "inject-cluster-action", node, task, rsc);
 666     pcmk__update_graph(graph, action);
 667     return TRUE;
 668 }
 669 
 670 /*!
 671  * \internal
 672  * \brief Simulate successfully executing a fencing action
 673  *
 674  * \param[in] graph   Graph to update with action result
 675  * \param[in] action  Fencing action to simulate
 676  *
 677  * \return TRUE
 678  */
 679 static gboolean
 680 simulate_fencing_action(crm_graph_t *graph, crm_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 681 {
 682     const char *op = crm_meta_value(action->params, "stonith_action");
 683     char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
 684 
 685     out->message(out, "inject-fencing-action", target, op);
 686 
 687     if (!pcmk__str_eq(op, "on", pcmk__str_casei)) {
 688         int rc = pcmk_ok;
 689         char xpath[STATUS_PATH_MAX];
 690 
 691         // Set node state to offline
 692         xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
 693                                                            false);
 694 
 695         CRM_ASSERT(cib_node != NULL);
 696         crm_xml_add(cib_node, XML_ATTR_ORIGIN, __func__);
 697         rc = fake_cib->cmds->replace(fake_cib, XML_CIB_TAG_STATUS, cib_node,
 698                                      cib_sync_call|cib_scope_local);
 699         CRM_ASSERT(rc == pcmk_ok);
 700 
 701         // Simulate controller clearing node's resource history and attributes
 702         snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s",
 703                  target, XML_CIB_TAG_LRM);
 704         fake_cib->cmds->remove(fake_cib, xpath, NULL,
 705                                cib_xpath|cib_sync_call|cib_scope_local);
 706 
 707         snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s",
 708                  target, XML_TAG_TRANSIENT_NODEATTRS);
 709         fake_cib->cmds->remove(fake_cib, xpath, NULL,
 710                                cib_xpath|cib_sync_call|cib_scope_local);
 711 
 712         free_xml(cib_node);
 713     }
 714 
 715     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
 716     pcmk__update_graph(graph, action);
 717     free(target);
 718     return TRUE;
 719 }
 720 
 721 enum transition_status
 722 pcmk__simulate_transition(pe_working_set_t *data_set, cib_t *cib,
     /* [previous][next][first][last][top][bottom][index][help] */
 723                           GList *op_fail_list)
 724 {
 725     crm_graph_t *transition = NULL;
 726     enum transition_status graph_rc;
 727 
 728     crm_graph_functions_t simulation_fns = {
 729         simulate_pseudo_action,
 730         simulate_resource_action,
 731         simulate_cluster_action,
 732         simulate_fencing_action,
 733     };
 734 
 735     out = data_set->priv;
 736 
 737     fake_cib = cib;
 738     fake_op_fail_list = op_fail_list;
 739 
 740     if (!out->is_quiet(out)) {
 741         out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
 742     }
 743 
 744     pcmk__set_graph_functions(&simulation_fns);
 745     transition = pcmk__unpack_graph(data_set->graph, crm_system_name);
 746     pcmk__log_graph(LOG_DEBUG, transition);
 747 
 748     fake_resource_list = data_set->resources;
 749     do {
 750         graph_rc = pcmk__execute_graph(transition);
 751     } while (graph_rc == transition_active);
 752     fake_resource_list = NULL;
 753 
 754     if (graph_rc != transition_complete) {
 755         out->err(out, "Transition failed: %s",
 756                  pcmk__graph_status2text(graph_rc));
 757         pcmk__log_graph(LOG_ERR, transition);
 758         out->err(out, "An invalid transition was produced");
 759     }
 760     pcmk__free_graph(transition);
 761 
 762     if (!out->is_quiet(out)) {
 763         // If not quiet, we'll need the resulting CIB for later display
 764         xmlNode *cib_object = NULL;
 765         int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
 766                                        cib_sync_call|cib_scope_local);
 767 
 768         CRM_ASSERT(rc == pcmk_ok);
 769         pe_reset_working_set(data_set);
 770         data_set->input = cib_object;
 771         out->end_list(out);
 772     }
 773     return graph_rc;
 774 }
 775 
 776 int
 777 pcmk__simulate(pe_working_set_t *data_set, pcmk__output_t *out,
     /* [previous][next][first][last][top][bottom][index][help] */
 778                pcmk_injections_t *injections, unsigned int flags,
 779                uint32_t section_opts, char *use_date, char *input_file,
 780                char *graph_file, char *dot_file)
 781 {
 782     int printed = pcmk_rc_no_output;
 783     int rc = pcmk_rc_ok;
 784     xmlNodePtr input = NULL;
 785     cib_t *cib = NULL;
 786 
 787     rc = cib__signon_query(&cib, &input);
 788     if (rc != pcmk_rc_ok) {
 789         goto simulate_done;
 790     }
 791 
 792     reset(data_set, input, out, use_date, flags);
 793     cluster_status(data_set);
 794 
 795     if (!out->is_quiet(out)) {
 796         if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
 797             printed = out->message(out, "maint-mode", data_set->flags);
 798         }
 799 
 800         if (data_set->disabled_resources || data_set->blocked_resources) {
 801             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 802             printed = out->info(out,
 803                                 "%d of %d resource instances DISABLED and "
 804                                 "%d BLOCKED from further action due to failure",
 805                                 data_set->disabled_resources,
 806                                 data_set->ninstances,
 807                                 data_set->blocked_resources);
 808         }
 809 
 810         /* Most formatted output headers use caps for each word, but this one
 811          * only has the first word capitalized for compatibility with pcs.
 812          */
 813         print_cluster_status(data_set,
 814                              pcmk_is_set(flags, pcmk_sim_show_pending)? pcmk_show_pending : 0,
 815                              section_opts, "Current cluster status",
 816                              (printed == pcmk_rc_ok));
 817         printed = pcmk_rc_ok;
 818     }
 819 
 820     // If the user requested any injections, handle them
 821     if ((injections->node_down != NULL)
 822         || (injections->node_fail != NULL)
 823         || (injections->node_up != NULL)
 824         || (injections->op_inject != NULL)
 825         || (injections->ticket_activate != NULL)
 826         || (injections->ticket_grant != NULL)
 827         || (injections->ticket_revoke != NULL)
 828         || (injections->ticket_standby != NULL)
 829         || (injections->watchdog != NULL)) {
 830 
 831         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 832         pcmk__inject_scheduler_input(data_set, cib, injections);
 833         printed = pcmk_rc_ok;
 834 
 835         rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
 836         if (rc != pcmk_rc_ok) {
 837             rc = pcmk_legacy2rc(rc);
 838             goto simulate_done;
 839         }
 840 
 841         cleanup_calculations(data_set);
 842         reset(data_set, input, out, use_date, flags);
 843         cluster_status(data_set);
 844     }
 845 
 846     if (input_file != NULL) {
 847         rc = write_xml_file(input, input_file, FALSE);
 848         if (rc < 0) {
 849             rc = pcmk_legacy2rc(rc);
 850             goto simulate_done;
 851         }
 852     }
 853 
 854     if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
 855         pcmk__output_t *logger_out = NULL;
 856         unsigned long long data_set_flags = pe_flag_no_compat;
 857 
 858         if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
 859             data_set_flags |= pe_flag_show_scores;
 860         }
 861         if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
 862             data_set_flags |= pe_flag_show_utilization;
 863         }
 864 
 865         if (pcmk_all_flags_set(data_set->flags,
 866                                pe_flag_show_scores|pe_flag_show_utilization)) {
 867             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 868             out->begin_list(out, NULL, NULL,
 869                             "Allocation Scores and Utilization Information");
 870             printed = pcmk_rc_ok;
 871 
 872         } else if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
 873             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 874             out->begin_list(out, NULL, NULL, "Allocation Scores");
 875             printed = pcmk_rc_ok;
 876 
 877         } else if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
 878             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 879             out->begin_list(out, NULL, NULL, "Utilization Information");
 880             printed = pcmk_rc_ok;
 881 
 882         } else {
 883             logger_out = pcmk__new_logger();
 884             if (logger_out == NULL) {
 885                 rc = pcmk_rc_error;
 886                 goto simulate_done;
 887             }
 888             data_set->priv = logger_out;
 889         }
 890 
 891         pcmk__schedule_actions(input, data_set_flags, data_set);
 892 
 893         if (logger_out == NULL) {
 894             out->end_list(out);
 895         } else {
 896             logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
 897             pcmk__output_free(logger_out);
 898             data_set->priv = out;
 899         }
 900 
 901         input = NULL;           /* Don't try and free it twice */
 902 
 903         if (graph_file != NULL) {
 904             rc = write_xml_file(data_set->graph, graph_file, FALSE);
 905             if (rc < 0) {
 906                 rc = pcmk_rc_graph_error;
 907                 goto simulate_done;
 908             }
 909         }
 910 
 911         if (dot_file != NULL) {
 912             rc = write_sim_dotfile(data_set, dot_file,
 913                                    pcmk_is_set(flags, pcmk_sim_all_actions),
 914                                    pcmk_is_set(flags, pcmk_sim_verbose));
 915             if (rc != pcmk_rc_ok) {
 916                 rc = pcmk_rc_dot_error;
 917                 goto simulate_done;
 918             }
 919         }
 920 
 921         if (!out->is_quiet(out)) {
 922             print_transition_summary(data_set, printed == pcmk_rc_ok);
 923         }
 924     }
 925 
 926     rc = pcmk_rc_ok;
 927 
 928     if (!pcmk_is_set(flags, pcmk_sim_simulate)) {
 929         goto simulate_done;
 930     }
 931 
 932     PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
 933     if (pcmk__simulate_transition(data_set, cib,
 934                                   injections->op_fail) != transition_complete) {
 935         rc = pcmk_rc_invalid_transition;
 936     }
 937 
 938     if (out->is_quiet(out)) {
 939         goto simulate_done;
 940     }
 941 
 942     set_effective_date(data_set, true, use_date);
 943 
 944     if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
 945         pe__set_working_set_flags(data_set, pe_flag_show_scores);
 946     }
 947     if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
 948         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
 949     }
 950 
 951     cluster_status(data_set);
 952     print_cluster_status(data_set, 0, section_opts, "Revised Cluster Status",
 953                          true);
 954 
 955 simulate_done:
 956     cib__clean_up_connection(&cib);
 957     return rc;
 958 }
 959 
 960 int
 961 pcmk_simulate(xmlNodePtr *xml, pe_working_set_t *data_set,
     /* [previous][next][first][last][top][bottom][index][help] */
 962               pcmk_injections_t *injections, unsigned int flags,
 963               unsigned int section_opts, char *use_date, char *input_file,
 964               char *graph_file, char *dot_file)
 965 {
 966     pcmk__output_t *out = NULL;
 967     int rc = pcmk_rc_ok;
 968 
 969     rc = pcmk__out_prologue(&out, xml);
 970     if (rc != pcmk_rc_ok) {
 971         return rc;
 972     }
 973 
 974     pe__register_messages(out);
 975     pcmk__register_lib_messages(out);
 976 
 977     rc = pcmk__simulate(data_set, out, injections, flags, section_opts,
 978                         use_date, input_file, graph_file, dot_file);
 979     pcmk__out_epilogue(out, xml, rc);
 980     return rc;
 981 }

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