LCOV - code coverage report
Current view: top level - pacemaker - pcmk_simulate.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 486 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 15 0.0 %

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

Generated by: LCOV version 1.14