diff --git a/include/pcmki/pcmki_simulate.h b/include/pcmki/pcmki_simulate.h
index 79859377ad..a58e27cd18 100644
--- a/include/pcmki/pcmki_simulate.h
+++ b/include/pcmki/pcmki_simulate.h
@@ -1,117 +1,70 @@
 /*
  * Copyright 2021 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #ifndef PCMKI_SIMULATE__H
 #  define PCMKI_SIMULATE__H
 
 #include <crm/common/output_internal.h>
 #include <crm/pengine/pe_types.h>
 #include <pacemaker.h>
 #include <stdbool.h>
 
-/**
- * \brief Write out a file in dot(1) format describing the actions that will
- *        be taken by the scheduler in response to an input CIB file.
- *
- * \param[in] data_set    Working set for the cluster.
- * \param[in] dot_file    The filename to write.
- * \param[in] all_actions Write all actions, even those that are optional or
- *                        are on unmanaged resources.
- * \param[in] verbose     Add extra information, such as action IDs, to the
- *                        output.
- *
- * \return Standard Pacemaker return code
- */
-int pcmk__write_sim_dotfile(pe_working_set_t *data_set, const char *dot_file,
-                            bool all_actions, bool verbose);
-
-/**
- * \brief Profile the configuration updates and scheduler actions in a single
- *        CIB file, printing the profiling timings.
- *
- * \note \p data_set->priv must have been set to a valid \p pcmk__output_t
- *       object before this function is called.
- *
- * \param[in] xml_file The CIB file to profile.
- * \param[in] repeat   Number of times to run.
- * \param[in] data_set Working set for the cluster.
- * \param[in] use_date The date to set the cluster's time to (may
- *                     be NULL).
- */
-void pcmk__profile_file(const char *xml_file, long long repeat, pe_working_set_t *data_set,
-                        char *use_date);
-
 /**
  * \brief Profile the configuration updates and scheduler actions in every
  *        CIB file in a given directory, printing the profiling timings for
  *        each.
  *
  * \note \p data_set->priv must have been set to a valid \p pcmk__output_t
  *       object before this function is called.
  *
  * \param[in] dir      A directory full of CIB files to be profiled.
  * \param[in] repeat   Number of times to run on each input file.
  * \param[in] data_set Working set for the cluster.
  * \param[in] use_date The date to set the cluster's time to (may
  *                     be NULL).
  */
 void pcmk__profile_dir(const char *dir, long long repeat, pe_working_set_t *data_set,
                        char *use_date);
 
-/**
- * \brief Set the date of the cluster, either to the value given by
- *        \p use_date, or to the "execution-date" value in the CIB.
- *
- * \note \p data_set->priv must have been set to a valid \p pcmk__output_t
- *       object before this function is called.
- *
- * \param[in,out] data_set       Working set for the cluster.
- * \param[in]     print_original If \p true, the "execution-date" should
- *                               also be printed.
- * \param[in]     use_date       The date to set the cluster's time to
- *                               (may be NULL).
- */
-void pcmk__set_effective_date(pe_working_set_t *data_set, bool print_original, char *use_date);
-
 /**
  * \brief Simulate a cluster's response to events.
  *
  * This high-level function essentially implements crm_simulate(8).  It operates
  * on an input CIB file and various lists of events that can be simulated.  It
  * optionally writes out a variety of artifacts to show the results of the
  * simulation.  Output can be modified with various flags.
  *
  * \param[in,out] data_set     Working set for the cluster.
  * \param[in,out] out          The output functions structure.
  * \param[in]     events       A structure containing cluster events
  *                             (node up/down, tickets, injected operations)
  *                             and related data.
  * \param[in]     flags        A bitfield of :pcmk_sim_flags to modify
  *                             operation of the simulation.
  * \param[in]     section_opts Which portions of the cluster status output
  *                             should be displayed?
  * \param[in]     use_date     The date to set the cluster's time to
  *                             (may be NULL).
  * \param[in]     input_file   The source CIB file, which may be overwritten by
  *                             this function (may be NULL).
  * \param[in]     graph_file   Where to write the XML-formatted transition graph
  *                             (may be NULL, in which case no file will be
  *                             written).
  * \param[in]     dot_file     Where to write the dot(1) formatted transition
  *                             graph (may be NULL, in which case no file will
  *                             be written).  See \p pcmk__write_sim_dotfile().
  *
  * \return Standard Pacemaker return code
  */
 int pcmk__simulate(pe_working_set_t *data_set, pcmk__output_t *out,
                    pcmk_injections_t *injections, unsigned int flags,
                    unsigned int section_opts, char *use_date, char *input_file,
                    char *graph_file, char *dot_file);
 
 #endif
diff --git a/lib/pacemaker/pcmk_simulate.c b/lib/pacemaker/pcmk_simulate.c
index babeb36289..37d2ac00c1 100644
--- a/lib/pacemaker/pcmk_simulate.c
+++ b/lib/pacemaker/pcmk_simulate.c
@@ -1,829 +1,872 @@
 /*
  * Copyright 2021-2022 the Pacemaker project contributors
  *
  * The version control history for this file may have further details.
  *
  * This source code is licensed under the GNU Lesser General Public License
  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
  */
 
 #include <crm_internal.h>
 #include <crm/cib/internal.h>
 #include <crm/common/output.h>
 #include <crm/common/results.h>
 #include <crm/pengine/pe_types.h>
 #include <pacemaker-internal.h>
 #include <pacemaker.h>
 
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
 #include "libpacemaker_private.h"
 
 #define STATUS_PATH_MAX 512
 
 static pcmk__output_t *out = NULL;
 static cib_t *fake_cib = NULL;
 static GList *fake_resource_list = NULL;
 static GList *fake_op_fail_list = NULL;
 
+static void set_effective_date(pe_working_set_t *data_set, bool print_original,
+                               char *use_date);
+
 static char *
 create_action_name(pe_action_t *action, bool verbose)
 {
     char *action_name = NULL;
     const char *prefix = "";
     const char *action_host = NULL;
     const char *clone_name = NULL;
     const char *task = action->task;
 
     if (action->node) {
         action_host = action->node->details->uname;
     } else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
         action_host = "<none>";
     }
 
     if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_casei)) {
         prefix = "Cancel ";
         task = action->cancel_task;
     }
 
     if (action->rsc && action->rsc->clone_name) {
         clone_name = action->rsc->clone_name;
     }
 
     if (clone_name) {
         char *key = NULL;
         guint interval_ms = 0;
 
         if (pcmk__guint_from_hash(action->meta,
                                   XML_LRM_ATTR_INTERVAL_MS, 0,
                                   &interval_ms) != pcmk_rc_ok) {
             interval_ms = 0;
         }
 
         if (pcmk__strcase_any_of(action->task, RSC_NOTIFY, RSC_NOTIFIED, NULL)) {
             const char *n_type = g_hash_table_lookup(action->meta, "notify_key_type");
             const char *n_task = g_hash_table_lookup(action->meta, "notify_key_operation");
 
             CRM_ASSERT(n_type != NULL);
             CRM_ASSERT(n_task != NULL);
             key = pcmk__notify_key(clone_name, n_type, n_task);
 
         } else {
             key = pcmk__op_key(clone_name, task, interval_ms);
         }
 
         if (action_host) {
             action_name = crm_strdup_printf("%s%s %s", prefix, key, action_host);
         } else {
             action_name = crm_strdup_printf("%s%s", prefix, key);
         }
         free(key);
 
     } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
         const char *op = g_hash_table_lookup(action->meta, "stonith_action");
 
         action_name = crm_strdup_printf("%s%s '%s' %s", prefix, action->task, op, action_host);
 
     } else if (action->rsc && action_host) {
         action_name = crm_strdup_printf("%s%s %s", prefix, action->uuid, action_host);
 
     } else if (action_host) {
         action_name = crm_strdup_printf("%s%s %s", prefix, action->task, action_host);
 
     } else {
         action_name = crm_strdup_printf("%s", action->uuid);
     }
 
     if (verbose) {
         char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
 
         free(action_name);
         action_name = with_id;
     }
     return action_name;
 }
 
 static void
 print_cluster_status(pe_working_set_t * data_set, unsigned int show_opts,
                      unsigned int section_opts, const char *title, bool print_spacer)
 {
     pcmk__output_t *out = data_set->priv;
     GList *all = NULL;
 
     section_opts |= pcmk_section_nodes | pcmk_section_resources;
 
     all = g_list_prepend(all, (gpointer) "*");
 
     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
     out->begin_list(out, NULL, NULL, "%s", title);
     out->message(out, "cluster-status", data_set, 0, NULL, FALSE,
                  section_opts,
                  show_opts | pcmk_show_inactive_rscs | pcmk_show_failed_detail,
                  NULL, all, all);
     out->end_list(out);
 
     g_list_free(all);
 }
 
 static void
 print_transition_summary(pe_working_set_t *data_set, bool print_spacer)
 {
     pcmk__output_t *out = data_set->priv;
 
     PCMK__OUTPUT_SPACER_IF(out, print_spacer);
     out->begin_list(out, NULL, NULL, "Transition Summary");
     pcmk__output_actions(data_set);
     out->end_list(out);
 }
 
 static void
 reset(pe_working_set_t *data_set, xmlNodePtr input, pcmk__output_t *out,
       char *use_date, unsigned int flags)
 {
     data_set->input = input;
     data_set->priv = out;
-    pcmk__set_effective_date(data_set, true, use_date);
+    set_effective_date(data_set, true, use_date);
     if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
         pe__set_working_set_flags(data_set, pe_flag_sanitized);
     }
     if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
         pe__set_working_set_flags(data_set, pe_flag_show_scores);
     }
     if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
         pe__set_working_set_flags(data_set, pe_flag_show_utilization);
     }
 }
 
-int
-pcmk__write_sim_dotfile(pe_working_set_t *data_set, const char *dot_file,
-                        bool all_actions, bool verbose)
+/*!
+ * \brief Write out a file in dot(1) format describing the actions that will
+ *        be taken by the scheduler in response to an input CIB file.
+ *
+ * \param[in] data_set     Working set for the cluster
+ * \param[in] dot_file     The filename to write
+ * \param[in] all_actions  Write all actions, even those that are optional or
+ *                         are on unmanaged resources
+ * \param[in] verbose      Add extra information, such as action IDs, to the
+ *                         output
+ *
+ * \return Standard Pacemaker return code
+ */
+static int
+write_sim_dotfile(pe_working_set_t *data_set, const char *dot_file,
+                  bool all_actions, bool verbose)
 {
     GList *gIter = NULL;
     FILE *dot_strm = fopen(dot_file, "w");
 
     if (dot_strm == NULL) {
         return errno;
     }
 
     fprintf(dot_strm, " digraph \"g\" {\n");
     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
         pe_action_t *action = (pe_action_t *) gIter->data;
         const char *style = "dashed";
         const char *font = "black";
         const char *color = "black";
         char *action_name = create_action_name(action, verbose);
 
         if (pcmk_is_set(action->flags, pe_action_pseudo)) {
             font = "orange";
         }
 
         if (pcmk_is_set(action->flags, pe_action_dumped)) {
             style = "bold";
             color = "green";
 
         } else if ((action->rsc != NULL)
                    && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
             color = "red";
             font = "purple";
             if (!all_actions) {
                 goto do_not_write;
             }
 
         } else if (pcmk_is_set(action->flags, pe_action_optional)) {
             color = "blue";
             if (!all_actions) {
                 goto do_not_write;
             }
 
         } else {
             color = "red";
             CRM_LOG_ASSERT(!pcmk_is_set(action->flags, pe_action_runnable));
         }
 
         pe__set_action_flags(action, pe_action_dumped);
         fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
                 action_name, style, color, font);
   do_not_write:
         free(action_name);
     }
 
     for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
         pe_action_t *action = (pe_action_t *) gIter->data;
 
         GList *gIter2 = NULL;
 
         for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
             pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
 
             char *before_name = NULL;
             char *after_name = NULL;
             const char *style = "dashed";
             bool optional = true;
 
             if (before->state == pe_link_dumped) {
                 optional = false;
                 style = "bold";
             } else if (before->type == pe_order_none) {
                 continue;
             } else if (pcmk_is_set(before->action->flags, pe_action_dumped)
                        && pcmk_is_set(action->flags, pe_action_dumped)
                        && before->type != pe_order_load) {
                 optional = false;
             }
 
             if (all_actions || !optional) {
                 before_name = create_action_name(before->action, verbose);
                 after_name = create_action_name(action, verbose);
                 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
                         before_name, after_name, style);
                 free(before_name);
                 free(after_name);
             }
         }
     }
 
     fprintf(dot_strm, "}\n");
     fflush(dot_strm);
     fclose(dot_strm);
     return pcmk_rc_ok;
 }
 
-void
-pcmk__profile_file(const char *xml_file, long long repeat, pe_working_set_t *data_set, char *use_date)
+/*!
+ * \brief Profile the configuration updates and scheduler actions in a single
+ *        CIB file, printing the profiling timings.
+ *
+ * \note \p data_set->priv must have been set to a valid \p pcmk__output_t
+ *       object before this function is called.
+ *
+ * \param[in] xml_file  The CIB file to profile
+ * \param[in] repeat    Number of times to run
+ * \param[in] data_set  Working set for the cluster
+ * \param[in] use_date  The date to set the cluster's time to (may be NULL)
+ */
+static void
+profile_file(const char *xml_file, long long repeat, pe_working_set_t *data_set,
+             char *use_date)
 {
     pcmk__output_t *out = data_set->priv;
     xmlNode *cib_object = NULL;
     clock_t start = 0;
     clock_t end;
 
     CRM_ASSERT(out != NULL);
 
     cib_object = filename2xml(xml_file);
     start = clock();
 
     if (pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS) == NULL) {
         create_xml_node(cib_object, XML_CIB_TAG_STATUS);
     }
 
     if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
         free_xml(cib_object);
         return;
     }
 
     if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
         free_xml(cib_object);
         return;
     }
 
     for (int i = 0; i < repeat; ++i) {
         xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
 
         data_set->input = input;
-        pcmk__set_effective_date(data_set, false, use_date);
+        set_effective_date(data_set, false, use_date);
         pcmk__schedule_actions(data_set, input, NULL);
         pe_reset_working_set(data_set);
     }
 
     end = clock();
     out->message(out, "profile", xml_file, start, end);
 }
 
 void
 pcmk__profile_dir(const char *dir, long long repeat, pe_working_set_t *data_set, char *use_date)
 {
     pcmk__output_t *out = data_set->priv;
     struct dirent **namelist;
 
     int file_num = scandir(dir, &namelist, 0, alphasort);
 
     CRM_ASSERT(out != NULL);
 
     if (file_num > 0) {
         struct stat prop;
         char buffer[FILENAME_MAX];
 
         out->begin_list(out, NULL, NULL, "Timings");
 
         while (file_num--) {
             if ('.' == namelist[file_num]->d_name[0]) {
                 free(namelist[file_num]);
                 continue;
 
             } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
                                             ".xml")) {
                 free(namelist[file_num]);
                 continue;
             }
             snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
             if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
-                pcmk__profile_file(buffer, repeat, data_set, use_date);
+                profile_file(buffer, repeat, data_set, use_date);
             }
             free(namelist[file_num]);
         }
         free(namelist);
 
         out->end_list(out);
     }
 }
 
-void
-pcmk__set_effective_date(pe_working_set_t *data_set, bool print_original, char *use_date)
+/*!
+ * \brief Set the date of the cluster, either to the value given by
+ *        \p use_date, or to the "execution-date" value in the CIB.
+ *
+ * \note \p data_set->priv must have been set to a valid \p pcmk__output_t
+ *       object before this function is called.
+ *
+ * \param[in,out] data_set        Working set for the cluster
+ * \param[in]     print_original  If \p true, the "execution-date" should
+ *                                also be printed
+ * \param[in]     use_date        The date to set the cluster's time to
+ *                                (may be NULL)
+ */
+static void
+set_effective_date(pe_working_set_t *data_set, bool print_original,
+                   char *use_date)
 {
     pcmk__output_t *out = data_set->priv;
     time_t original_date = 0;
 
     CRM_ASSERT(out != NULL);
 
     crm_element_value_epoch(data_set->input, "execution-date", &original_date);
 
     if (use_date) {
         data_set->now = crm_time_new(use_date);
         out->info(out, "Setting effective cluster time: %s", use_date);
         crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
                      crm_time_log_date | crm_time_log_timeofday);
 
     } else if (original_date) {
 
         data_set->now = crm_time_new(NULL);
         crm_time_set_timet(data_set->now, &original_date);
 
         if (print_original) {
             char *when = crm_time_as_string(data_set->now,
                             crm_time_log_date|crm_time_log_timeofday);
 
             out->info(out, "Using the original execution date of: %s", when);
             free(when);
         }
     }
 }
 
 static gboolean
 exec_pseudo_action(crm_graph_t * graph, crm_action_t * action)
 {
     const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
     const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
 
     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
     out->message(out, "inject-pseudo-action", node, task);
 
     pcmk__update_graph(graph, action);
     return TRUE;
 }
 
 static gboolean
 exec_rsc_action(crm_graph_t * graph, crm_action_t * action)
 {
     int rc = 0;
     GList *gIter = NULL;
     lrmd_event_data_t *op = NULL;
     int target_outcome = PCMK_OCF_OK;
 
     const char *rtype = NULL;
     const char *rclass = NULL;
     const char *resource = NULL;
     const char *rprovider = NULL;
     const char *lrm_name = NULL;
     const char *operation = crm_element_value(action->xml, "operation");
     const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC);
 
     xmlNode *cib_node = NULL;
     xmlNode *cib_resource = NULL;
     xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
 
     char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
     char *uuid = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET_UUID);
     const char *router_node = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE);
 
     if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) {
         crm_info("Skipping %s op for %s", operation, node);
         goto done;
     }
 
     if (action_rsc == NULL) {
         crm_log_xml_err(action->xml, "Bad");
         free(node); free(uuid);
         return FALSE;
     }
 
     /* Look for the preferred name
      * If not found, try the expected 'local' name
      * If not found use the preferred name anyway
      */
     resource = crm_element_value(action_rsc, XML_ATTR_ID);
     CRM_ASSERT(resource != NULL); // makes static analysis happy
     lrm_name = resource; // Preferred name when writing history
     if (pe_find_resource(fake_resource_list, resource) == NULL) {
         const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG);
 
         if (longname && pe_find_resource(fake_resource_list, longname)) {
             resource = longname;
         }
     }
 
     if (pcmk__strcase_any_of(operation, "delete", RSC_METADATA, NULL)) {
         out->message(out, "inject-rsc-action", resource, operation, node, (guint) 0);
         goto done;
     }
 
     rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS);
     rtype = crm_element_value(action_rsc, XML_ATTR_TYPE);
     rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER);
 
     pcmk__scan_min_int(target_rc_s, &target_outcome, 0);
 
     CRM_ASSERT(fake_cib->cmds->query(fake_cib, NULL, NULL, cib_sync_call | cib_scope_local) ==
                pcmk_ok);
 
     cib_node = pcmk__inject_node(fake_cib, node,
                                  ((router_node == NULL)? uuid: node));
     CRM_ASSERT(cib_node != NULL);
 
     cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
                                                  lrm_name, rclass, rtype,
                                                  rprovider);
     if (cib_resource == NULL) {
         crm_err("invalid resource in transition");
         free(node); free(uuid);
         free_xml(cib_node);
         return FALSE;
     }
 
     op = pcmk__event_from_graph_action(cib_resource, action, PCMK_EXEC_DONE,
                                        target_outcome, "User-injected result");
 
     out->message(out, "inject-rsc-action", resource, op->op_type, node, op->interval_ms);
 
     for (gIter = fake_op_fail_list; gIter != NULL; gIter = gIter->next) {
         char *spec = (char *)gIter->data;
         char *key = NULL;
         const char *match_name = NULL;
 
         // Allow user to specify anonymous clone with or without instance number
         key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
                                 op->interval_ms, node);
         if (strncasecmp(key, spec, strlen(key)) == 0) {
             match_name = resource;
         }
         free(key);
 
         if ((match_name == NULL) && strcmp(resource, lrm_name)) {
             key = crm_strdup_printf(PCMK__OP_FMT "@%s=", lrm_name, op->op_type,
                                     op->interval_ms, node);
             if (strncasecmp(key, spec, strlen(key)) == 0) {
                 match_name = lrm_name;
             }
             free(key);
         }
 
         if (match_name != NULL) {
 
             rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
             // ${match_name}_${task}_${interval_in_ms}@${node}=${rc}
 
             if (rc != 1) {
                 out->err(out,
                          "Invalid failed operation spec: %s. Result code must be integer",
                          spec);
                 continue;
             }
             crm__set_graph_action_flags(action, pcmk__graph_action_failed);
             graph->abort_priority = INFINITY;
             out->info(out, "Pretending action %d failed with rc=%d", action->id, op->rc);
             pcmk__inject_failcount(out, cib_node, match_name, op->op_type,
                                    op->interval_ms, op->rc);
             break;
         }
     }
 
     pcmk__inject_action_result(cib_resource, op, target_outcome);
     lrmd_free_event(op);
 
     rc = fake_cib->cmds->modify(fake_cib, XML_CIB_TAG_STATUS, cib_node,
                                   cib_sync_call | cib_scope_local);
     CRM_ASSERT(rc == pcmk_ok);
 
   done:
     free(node); free(uuid);
     free_xml(cib_node);
     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
     pcmk__update_graph(graph, action);
     return TRUE;
 }
 
 static gboolean
 exec_crmd_action(crm_graph_t * graph, crm_action_t * action)
 {
     const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
     const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
     xmlNode *rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
 
     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
     out->message(out, "inject-cluster-action", node, task, rsc);
     pcmk__update_graph(graph, action);
     return TRUE;
 }
 
 static gboolean
 exec_stonith_action(crm_graph_t * graph, crm_action_t * action)
 {
     const char *op = crm_meta_value(action->params, "stonith_action");
     char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
 
     out->message(out, "inject-fencing-action", target, op);
 
     if(!pcmk__str_eq(op, "on", pcmk__str_casei)) {
         int rc = 0;
         char xpath[STATUS_PATH_MAX];
         xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
                                                            false);
 
         crm_xml_add(cib_node, XML_ATTR_ORIGIN, __func__);
         CRM_ASSERT(cib_node != NULL);
 
         rc = fake_cib->cmds->replace(fake_cib, XML_CIB_TAG_STATUS, cib_node,
                                    cib_sync_call | cib_scope_local);
         CRM_ASSERT(rc == pcmk_ok);
 
         snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM);
         fake_cib->cmds->remove(fake_cib, xpath, NULL,
                                       cib_xpath | cib_sync_call | cib_scope_local);
 
         snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target,
                  XML_TAG_TRANSIENT_NODEATTRS);
         fake_cib->cmds->remove(fake_cib, xpath, NULL,
                                       cib_xpath | cib_sync_call | cib_scope_local);
 
         free_xml(cib_node);
     }
 
     crm__set_graph_action_flags(action, pcmk__graph_action_confirmed);
     pcmk__update_graph(graph, action);
     free(target);
     return TRUE;
 }
 
 enum transition_status
 run_simulation(pe_working_set_t * data_set, cib_t *cib, GList *op_fail_list)
 {
     crm_graph_t *transition = NULL;
     enum transition_status graph_rc;
 
     crm_graph_functions_t exec_fns = {
         exec_pseudo_action,
         exec_rsc_action,
         exec_crmd_action,
         exec_stonith_action,
     };
 
     out = data_set->priv;
 
     fake_cib = cib;
     fake_op_fail_list = op_fail_list;
 
     if (!out->is_quiet(out)) {
         out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
     }
 
     pcmk__set_graph_functions(&exec_fns);
     transition = pcmk__unpack_graph(data_set->graph, crm_system_name);
     pcmk__log_graph(LOG_DEBUG, transition);
 
     fake_resource_list = data_set->resources;
     do {
         graph_rc = pcmk__execute_graph(transition);
 
     } while (graph_rc == transition_active);
     fake_resource_list = NULL;
 
     if (graph_rc != transition_complete) {
         out->err(out, "Transition failed: %s",
                  pcmk__graph_status2text(graph_rc));
         pcmk__log_graph(LOG_ERR, transition);
     }
     pcmk__free_graph(transition);
     if (graph_rc != transition_complete) {
         out->err(out, "An invalid transition was produced");
     }
 
     if (!out->is_quiet(out)) {
         xmlNode *cib_object = NULL;
         int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object, cib_sync_call | cib_scope_local);
 
         CRM_ASSERT(rc == pcmk_ok);
         pe_reset_working_set(data_set);
         data_set->input = cib_object;
 
         out->end_list(out);
     }
 
     return graph_rc;
 }
 
 int
 pcmk__simulate(pe_working_set_t *data_set, pcmk__output_t *out, pcmk_injections_t *injections,
                unsigned int flags, unsigned int section_opts, char *use_date,
                char *input_file, char *graph_file, char *dot_file)
 {
     int printed = pcmk_rc_no_output;
     int rc = pcmk_rc_ok;
     xmlNodePtr input = NULL;
     cib_t *cib = NULL;
     bool modified = false;
 
     rc = cib__signon_query(&cib, &input);
     if (rc != pcmk_rc_ok) {
         goto simulate_done;
     }
 
     reset(data_set, input, out, use_date, flags);
     cluster_status(data_set);
 
     if (!out->is_quiet(out)) {
         if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
             printed = out->message(out, "maint-mode", data_set->flags);
         }
 
         if (data_set->disabled_resources || data_set->blocked_resources) {
             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
             printed = out->info(out, "%d of %d resource instances DISABLED and %d BLOCKED "
                                 "from further action due to failure",
                                 data_set->disabled_resources, data_set->ninstances,
                                 data_set->blocked_resources);
         }
 
         /* Most formatted output headers use caps for each word, but this one
          * only has the first word capitalized for compatibility with pcs.
          */
         print_cluster_status(data_set, pcmk_is_set(flags, pcmk_sim_show_pending) ? pcmk_show_pending : 0,
                              section_opts, "Current cluster status", printed == pcmk_rc_ok);
         printed = pcmk_rc_ok;
     }
 
     modified = injections->node_down != NULL || injections->node_fail != NULL ||
                injections->node_up != NULL || injections->op_inject != NULL ||
                injections->ticket_activate != NULL || injections->ticket_grant != NULL ||
                injections->ticket_revoke != NULL || injections->ticket_standby != NULL ||
                injections->watchdog != NULL || injections->watchdog != NULL;
 
     if (modified) {
         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
         modify_configuration(data_set, cib, injections);
         printed = pcmk_rc_ok;
 
         rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
         if (rc != pcmk_rc_ok) {
             rc = pcmk_legacy2rc(rc);
             goto simulate_done;
         }
 
         cleanup_calculations(data_set);
         reset(data_set, input, out, use_date, flags);
         cluster_status(data_set);
     }
 
     if (input_file != NULL) {
         rc = write_xml_file(input, input_file, FALSE);
         if (rc < 0) {
             rc = pcmk_legacy2rc(rc);
             goto simulate_done;
         }
     }
 
     if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
         crm_time_t *local_date = NULL;
         pcmk__output_t *logger_out = NULL;
 
         if (pcmk_all_flags_set(data_set->flags, pe_flag_show_scores|pe_flag_show_utilization)) {
             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
             out->begin_list(out, NULL, NULL, "Allocation Scores and Utilization Information");
             printed = pcmk_rc_ok;
         } else if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
             out->begin_list(out, NULL, NULL, "Allocation Scores");
             printed = pcmk_rc_ok;
         } else if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
             PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
             out->begin_list(out, NULL, NULL, "Utilization Information");
             printed = pcmk_rc_ok;
         } else {
             logger_out = pcmk__new_logger();
             if (logger_out == NULL) {
                 rc = pcmk_rc_error;
                 goto simulate_done;
             }
 
             data_set->priv = logger_out;
         }
 
         pcmk__schedule_actions(data_set, input, local_date);
 
         if (logger_out == NULL) {
             out->end_list(out);
         } else {
             logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
             pcmk__output_free(logger_out);
             data_set->priv = out;
         }
 
         input = NULL;           /* Don't try and free it twice */
 
         if (graph_file != NULL) {
             rc = write_xml_file(data_set->graph, graph_file, FALSE);
             if (rc < 0) {
                 rc = pcmk_rc_graph_error;
                 goto simulate_done;
             }
         }
 
         if (dot_file != NULL) {
-            rc = pcmk__write_sim_dotfile(data_set, dot_file,
-                                         pcmk_is_set(flags, pcmk_sim_all_actions),
-                                         pcmk_is_set(flags, pcmk_sim_verbose));
+            rc = write_sim_dotfile(data_set, dot_file,
+                                   pcmk_is_set(flags, pcmk_sim_all_actions),
+                                   pcmk_is_set(flags, pcmk_sim_verbose));
             if (rc != pcmk_rc_ok) {
                 rc = pcmk_rc_dot_error;
                 goto simulate_done;
             }
         }
 
         if (!out->is_quiet(out)) {
             print_transition_summary(data_set, printed == pcmk_rc_ok);
         }
     }
 
     rc = pcmk_rc_ok;
 
     if (pcmk_is_set(flags, pcmk_sim_simulate)) {
         PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
         if (run_simulation(data_set, cib, injections->op_fail) != transition_complete) {
             rc = pcmk_rc_invalid_transition;
         }
 
         if (!out->is_quiet(out)) {
-            pcmk__set_effective_date(data_set, true, use_date);
+            set_effective_date(data_set, true, use_date);
 
             if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
                 pe__set_working_set_flags(data_set, pe_flag_show_scores);
             }
             if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
                 pe__set_working_set_flags(data_set, pe_flag_show_utilization);
             }
 
             cluster_status(data_set);
             print_cluster_status(data_set, 0, section_opts, "Revised Cluster Status", true);
         }
     }
 
 simulate_done:
     if (cib) {
         cib->cmds->signoff(cib);
         cib_delete(cib);
     }
 
     return rc;
 }
 
 int
 pcmk_simulate(xmlNodePtr *xml, pe_working_set_t *data_set, pcmk_injections_t *injections,
               unsigned int flags, unsigned int section_opts, char *use_date,
               char *input_file, char *graph_file, char *dot_file)
 {
     pcmk__output_t *out = NULL;
     int rc = pcmk_rc_ok;
 
     rc = pcmk__out_prologue(&out, xml);
     if (rc != pcmk_rc_ok) {
         return rc;
     }
 
     pe__register_messages(out);
     pcmk__register_lib_messages(out);
 
     rc = pcmk__simulate(data_set, out, injections, flags, section_opts,
                         use_date, input_file, graph_file, dot_file);
     pcmk__out_epilogue(out, xml, rc);
     return rc;
 }
 
 void
 pcmk_free_injections(pcmk_injections_t *injections)
 {
     if (injections == NULL) {
         return;
     }
 
     g_list_free_full(injections->node_up, g_free);
     g_list_free_full(injections->node_down, g_free);
     g_list_free_full(injections->node_fail, g_free);
     g_list_free_full(injections->op_fail, g_free);
     g_list_free_full(injections->op_inject, g_free);
     g_list_free_full(injections->ticket_grant, g_free);
     g_list_free_full(injections->ticket_revoke, g_free);
     g_list_free_full(injections->ticket_standby, g_free);
     g_list_free_full(injections->ticket_activate, g_free);
     free(injections->quorum);
     free(injections->watchdog);
 
     free(injections);
 }