pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pcmk_simulate.c
Go to the documentation of this file.
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 
43 static char *
44 create_action_name(const pcmk_action_t *action, bool verbose)
45 {
46  char *action_name = NULL;
47  const char *prefix = "";
48  const char *action_host = NULL;
49  const char *history_id = NULL;
50  const char *task = action->task;
51 
52  if (action->node != NULL) {
53  action_host = action->node->priv->name;
54  } else if (!pcmk_is_set(action->flags, pcmk__action_pseudo)) {
55  action_host = "<none>";
56  }
57 
58  if (pcmk__str_eq(action->task, PCMK_ACTION_CANCEL, pcmk__str_none)) {
59  prefix = "Cancel ";
60  task = action->cancel_task;
61  }
62 
63  if (action->rsc != NULL) {
64  history_id = action->rsc->priv->history_id;
65  }
66 
67  if (history_id != NULL) {
68  char *key = NULL;
69  guint interval_ms = 0;
70 
72  &interval_ms) != pcmk_rc_ok) {
73  interval_ms = 0;
74  }
75 
77  PCMK_ACTION_NOTIFIED, NULL)) {
78  const char *n_type = g_hash_table_lookup(action->meta,
79  "notify_key_type");
80  const char *n_task = g_hash_table_lookup(action->meta,
81  "notify_key_operation");
82 
83  pcmk__assert((n_type != NULL) && (n_task != NULL));
84  key = pcmk__notify_key(history_id, n_type, n_task);
85  } else {
86  key = pcmk__op_key(history_id, task, interval_ms);
87  }
88 
89  if (action_host != NULL) {
90  action_name = crm_strdup_printf("%s%s %s",
91  prefix, key, action_host);
92  } else {
93  action_name = crm_strdup_printf("%s%s", prefix, key);
94  }
95  free(key);
96 
97  } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
98  pcmk__str_none)) {
99  const char *op = g_hash_table_lookup(action->meta,
101 
102  action_name = crm_strdup_printf("%s%s '%s' %s",
103  prefix, action->task, op, action_host);
104 
105  } else if (action->rsc && action_host) {
106  action_name = crm_strdup_printf("%s%s %s",
107  prefix, action->uuid, action_host);
108 
109  } else if (action_host) {
110  action_name = crm_strdup_printf("%s%s %s",
111  prefix, action->task, action_host);
112 
113  } else {
114  action_name = crm_strdup_printf("%s", action->uuid);
115  }
116 
117  if (verbose) {
118  char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
119 
120  free(action_name);
121  action_name = with_id;
122  }
123  return action_name;
124 }
125 
136 static void
137 print_cluster_status(pcmk_scheduler_t *scheduler, uint32_t show_opts,
138  uint32_t section_opts, const char *title,
139  bool print_spacer)
140 {
141  pcmk__output_t *out = scheduler->priv->out;
142  GList *all = NULL;
143  crm_exit_t stonith_rc = 0;
145 
146  section_opts |= pcmk_section_nodes | pcmk_section_resources;
148 
149  all = g_list_prepend(all, (gpointer) "*");
150 
151  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
152  out->begin_list(out, NULL, NULL, "%s", title);
153  out->message(out, "cluster-status",
154  scheduler, state, stonith_rc, NULL,
155  pcmk__fence_history_none, section_opts, show_opts, NULL,
156  all, all);
157  out->end_list(out);
158 
159  g_list_free(all);
160 }
161 
169 static void
170 print_transition_summary(pcmk_scheduler_t *scheduler, bool print_spacer)
171 {
172  pcmk__output_t *out = scheduler->priv->out;
173 
174  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
175  out->begin_list(out, NULL, NULL, "Transition Summary");
177  out->end_list(out);
178 }
179 
190 static void
191 reset(pcmk_scheduler_t *scheduler, xmlNodePtr input, pcmk__output_t *out,
192  const char *use_date, unsigned int flags)
193 {
194  scheduler->input = input;
195  scheduler->priv->out = out;
196  set_effective_date(scheduler, true, use_date);
199  }
202  }
205  }
206 }
207 
221 static int
222 write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file,
223  bool all_actions, bool verbose)
224 {
225  GList *iter = NULL;
226  FILE *dot_strm = fopen(dot_file, "w");
227 
228  if (dot_strm == NULL) {
229  return errno;
230  }
231 
232  fprintf(dot_strm, " digraph \"g\" {\n");
233  for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
234  pcmk_action_t *action = (pcmk_action_t *) iter->data;
235  const char *style = "dashed";
236  const char *font = "black";
237  const char *color = "black";
238  char *action_name = create_action_name(action, verbose);
239 
240  if (pcmk_is_set(action->flags, pcmk__action_pseudo)) {
241  font = "orange";
242  }
243 
245  style = PCMK__VALUE_BOLD;
246  color = "green";
247 
248  } else if ((action->rsc != NULL)
249  && !pcmk_is_set(action->rsc->flags, pcmk__rsc_managed)) {
250  color = "red";
251  font = "purple";
252  if (!all_actions) {
253  goto do_not_write;
254  }
255 
256  } else if (pcmk_is_set(action->flags, pcmk__action_optional)) {
257  color = "blue";
258  if (!all_actions) {
259  goto do_not_write;
260  }
261 
262  } else {
263  color = "red";
265  }
266 
268  fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
269  action_name, style, color, font);
270  do_not_write:
271  free(action_name);
272  }
273 
274  for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
275  pcmk_action_t *action = (pcmk_action_t *) iter->data;
276 
277  for (GList *before_iter = action->actions_before;
278  before_iter != NULL; before_iter = before_iter->next) {
279 
280  pcmk__related_action_t *before = before_iter->data;
281 
282  char *before_name = NULL;
283  char *after_name = NULL;
284  const char *style = "dashed";
285  bool optional = true;
286 
287  if (before->graphed) {
288  optional = false;
289  style = PCMK__VALUE_BOLD;
290  } else if (before->flags == pcmk__ar_none) {
291  continue;
292  } else if (pcmk_is_set(before->action->flags,
296  optional = false;
297  }
298 
299  if (all_actions || !optional) {
300  before_name = create_action_name(before->action, verbose);
301  after_name = create_action_name(action, verbose);
302  fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
303  before_name, after_name, style);
304  free(before_name);
305  free(after_name);
306  }
307  }
308  }
309 
310  fprintf(dot_strm, "}\n");
311  fflush(dot_strm);
312  fclose(dot_strm);
313  return pcmk_rc_ok;
314 }
315 
328 static void
329 profile_file(const char *xml_file, long long repeat,
330  pcmk_scheduler_t *scheduler, const char *use_date)
331 {
332  pcmk__output_t *out = scheduler->priv->out;
333  xmlNode *cib_object = NULL;
334  clock_t start = 0;
335  clock_t end;
336  unsigned long long scheduler_flags = pcmk__sched_none;
337 
338  pcmk__assert(out != NULL);
339 
340  cib_object = pcmk__xml_read(xml_file);
341  start = clock();
342 
343  if (pcmk_find_cib_element(cib_object, PCMK_XE_STATUS) == NULL) {
344  pcmk__xe_create(cib_object, PCMK_XE_STATUS);
345  }
346 
347  if (pcmk__update_configured_schema(&cib_object, false) != pcmk_rc_ok) {
348  pcmk__xml_free(cib_object);
349  return;
350  }
351 
352  if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) {
353  pcmk__xml_free(cib_object);
354  return;
355  }
356 
358  scheduler_flags |= pcmk__sched_output_scores;
359  }
361  scheduler_flags |= pcmk__sched_show_utilization;
362  }
363 
364  for (int i = 0; i < repeat; ++i) {
365  xmlNode *input = cib_object;
366 
367  if (repeat > 1) {
368  input = pcmk__xml_copy(NULL, cib_object);
369  }
370  scheduler->input = input;
371  set_effective_date(scheduler, false, use_date);
372  pcmk__schedule_actions(input, scheduler_flags, scheduler);
374  }
375 
376  end = clock();
377  out->message(out, "profile", xml_file, start, end);
378 }
379 
380 void
381 pcmk__profile_dir(const char *dir, long long repeat,
382  pcmk_scheduler_t *scheduler, const char *use_date)
383 {
384  pcmk__output_t *out = scheduler->priv->out;
385  struct dirent **namelist;
386 
387  int file_num = scandir(dir, &namelist, 0, alphasort);
388 
389  pcmk__assert(out != NULL);
390 
391  if (file_num > 0) {
392  struct stat prop;
393  char buffer[FILENAME_MAX];
394 
395  out->begin_list(out, NULL, NULL, "Timings");
396 
397  while (file_num--) {
398  if ('.' == namelist[file_num]->d_name[0]) {
399  free(namelist[file_num]);
400  continue;
401 
402  } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
403  ".xml")) {
404  free(namelist[file_num]);
405  continue;
406  }
407  snprintf(buffer, sizeof(buffer), "%s/%s",
408  dir, namelist[file_num]->d_name);
409  if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
410  profile_file(buffer, repeat, scheduler, use_date);
411  }
412  free(namelist[file_num]);
413  }
414  free(namelist);
415 
416  out->end_list(out);
417  }
418 }
419 
433 static void
434 set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
435  const char *use_date)
436 {
437  pcmk__output_t *out = scheduler->priv->out;
438  time_t original_date = 0;
439 
440  pcmk__assert(out != NULL);
441 
443  &original_date);
444 
445  if (use_date) {
446  scheduler->priv->now = crm_time_new(use_date);
447  out->info(out, "Setting effective cluster time: %s", use_date);
448  crm_time_log(LOG_NOTICE, "Pretending 'now' is", scheduler->priv->now,
450 
451  } else if (original_date != 0) {
452  scheduler->priv->now = pcmk__copy_timet(original_date);
453 
454  if (print_original) {
455  char *when = crm_time_as_string(scheduler->priv->now,
458 
459  out->info(out, "Using the original execution date of: %s", when);
460  free(when);
461  }
462  }
463 }
464 
474 static int
475 simulate_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
476 {
477  const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE);
478  const char *task = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
479 
481  out->message(out, "inject-pseudo-action", node, task);
482 
483  pcmk__update_graph(graph, action);
484  return pcmk_rc_ok;
485 }
486 
496 static int
497 simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
498 {
499  int rc;
500  lrmd_event_data_t *op = NULL;
501  int target_outcome = PCMK_OCF_OK;
502 
503  const char *rtype = NULL;
504  const char *rclass = NULL;
505  const char *resource = NULL;
506  const char *rprovider = NULL;
507  const char *resource_config_name = NULL;
508  const char *operation = crm_element_value(action->xml, PCMK_XA_OPERATION);
509  const char *target_rc_s = crm_meta_value(action->params,
511 
512  xmlNode *cib_node = NULL;
513  xmlNode *cib_resource = NULL;
514  xmlNode *action_rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE,
515  NULL, NULL);
516 
518  char *uuid = NULL;
519  const char *router_node = crm_element_value(action->xml,
521 
522  // Certain actions don't need to be displayed or history entries
523  if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) {
524  crm_debug("No history injection for %s op on %s", operation, node);
525  goto done; // Confirm action and update graph
526  }
527 
528  if (action_rsc == NULL) { // Shouldn't be possible
529  crm_log_xml_err(action->xml, "Bad");
530  free(node);
531  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  resource_config_name = crm_element_value(action_rsc, PCMK_XA_ID);
540  if (resource_config_name == NULL) { // Shouldn't be possible
541  crm_log_xml_err(action->xml, "No ID");
542  free(node);
543  return EPROTO;
544  }
545  resource = resource_config_name;
546  if (pe_find_resource(fake_resource_list, resource) == NULL) {
547  const char *longname = crm_element_value(action_rsc, PCMK__XA_LONG_ID);
548 
549  if ((longname != NULL)
550  && (pe_find_resource(fake_resource_list, longname) != NULL)) {
551  resource = longname;
552  }
553  }
554 
555  // Certain actions need to be displayed but don't need history entries
557  PCMK_ACTION_META_DATA, NULL)) {
558  out->message(out, "inject-rsc-action", resource, operation, node,
559  (guint) 0);
560  goto done; // Confirm action and update graph
561  }
562 
563  rclass = crm_element_value(action_rsc, PCMK_XA_CLASS);
564  rtype = crm_element_value(action_rsc, PCMK_XA_TYPE);
565  rprovider = crm_element_value(action_rsc, PCMK_XA_PROVIDER);
566 
567  pcmk__scan_min_int(target_rc_s, &target_outcome, 0);
568 
569  pcmk__assert(fake_cib->cmds->query(fake_cib, NULL, NULL,
570  cib_sync_call) == pcmk_ok);
571 
572  // Ensure the action node is in the CIB
574  cib_node = pcmk__inject_node(fake_cib, node,
575  ((router_node == NULL)? uuid: node));
576  free(uuid);
577  pcmk__assert(cib_node != NULL);
578 
579  // Add a history entry for the action
580  cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
581  resource_config_name,
582  rclass, rtype, rprovider);
583  if (cib_resource == NULL) {
584  crm_err("Could not simulate action %d history for resource %s",
585  action->id, resource);
586  free(node);
587  pcmk__xml_free(cib_node);
588  return EINVAL;
589  }
590 
591  // Simulate and display an executor event for the action result
593  target_outcome, "User-injected result");
594  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  for (const GList *iter = fake_op_fail_list;
599  iter != NULL; iter = iter->next) {
600  const char *spec = (const char *) iter->data;
601  char *key = NULL;
602  const char *match_name = NULL;
603  const char *offset = NULL;
604 
605  // Allow user to specify anonymous clone with or without instance number
606  key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
607  op->interval_ms, node);
608  if (strncasecmp(key, spec, strlen(key)) == 0) {
609  match_name = resource;
610  }
611  free(key);
612 
613  // If not found, try the resource's name in the configuration
614  if ((match_name == NULL)
615  && (strcmp(resource, resource_config_name) != 0)) {
616 
617  key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
618  op->op_type, op->interval_ms, node);
619  if (strncasecmp(key, spec, strlen(key)) == 0) {
620  match_name = resource_config_name;
621  }
622  free(key);
623  }
624 
625  if (match_name == NULL) {
626  continue; // This failed action entry doesn't match
627  }
628 
629  // ${match_name}_${task}_${interval_in_ms}@${node}=${rc}
630  rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
631  if (rc != 1) {
632  out->err(out, "Invalid failed operation '%s' "
633  "(result code must be integer)", spec);
634  continue; // Keep checking other list entries
635  }
636 
637  out->info(out, "Pretending action %d failed with rc=%d",
638  action->id, op->rc);
641 
642  if (pcmk__str_eq(op->op_type, PCMK_ACTION_START, pcmk__str_none)) {
643  offset = pcmk__s(graph->failed_start_offset, PCMK_VALUE_INFINITY);
644 
645  } else if (pcmk__str_eq(op->op_type, PCMK_ACTION_STOP,
646  pcmk__str_none)) {
647  offset = pcmk__s(graph->failed_stop_offset, PCMK_VALUE_INFINITY);
648  }
649 
650  pcmk__inject_failcount(out, fake_cib, cib_node, match_name, op->op_type,
651  op->interval_ms, op->rc,
652  pcmk_str_is_infinity(offset));
653  break;
654  }
655 
656  pcmk__inject_action_result(cib_resource, op, node, target_outcome);
657  lrmd_free_event(op);
658  rc = fake_cib->cmds->modify(fake_cib, PCMK_XE_STATUS, cib_node,
659  cib_sync_call);
660  pcmk__assert(rc == pcmk_ok);
661 
662  done:
663  free(node);
664  pcmk__xml_free(cib_node);
666  pcmk__update_graph(graph, action);
667  return pcmk_rc_ok;
668 }
669 
679 static int
680 simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
681 {
682  const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE);
683  const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
684  xmlNode *rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
685  NULL);
686 
688  out->message(out, "inject-cluster-action", node, task, rsc);
689  pcmk__update_graph(graph, action);
690  return pcmk_rc_ok;
691 }
692 
702 static int
703 simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
704 {
705  const char *op = crm_meta_value(action->params, PCMK__META_STONITH_ACTION);
707 
708  out->message(out, "inject-fencing-action", target, op);
709 
710  if (!pcmk__str_eq(op, PCMK_ACTION_ON, pcmk__str_casei)) {
711  int rc = pcmk_ok;
712  GString *xpath = g_string_sized_new(512);
713 
714  // Set node state to offline
715  xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
716  false);
717 
718  pcmk__assert(cib_node != NULL);
719  crm_xml_add(cib_node, PCMK_XA_CRM_DEBUG_ORIGIN, __func__);
720  rc = fake_cib->cmds->replace(fake_cib, PCMK_XE_STATUS, cib_node,
721  cib_sync_call);
722  pcmk__assert(rc == pcmk_ok);
723 
724  // Simulate controller clearing node's resource history and attributes
725  pcmk__g_strcat(xpath,
727  "[@" PCMK_XA_UNAME "='", target, "']/" PCMK__XE_LRM,
728  NULL);
729  fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
731 
732  g_string_truncate(xpath, 0);
733  pcmk__g_strcat(xpath,
735  "[@" PCMK_XA_UNAME "='", target, "']"
737  fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
739 
740  pcmk__xml_free(cib_node);
741  g_string_free(xpath, TRUE);
742  }
743 
745  pcmk__update_graph(graph, action);
746  free(target);
747  return pcmk_rc_ok;
748 }
749 
752  const GList *op_fail_list)
753 {
754  pcmk__graph_t *transition = NULL;
755  enum pcmk__graph_status graph_rc;
756 
757  pcmk__graph_functions_t simulation_fns = {
758  simulate_pseudo_action,
759  simulate_resource_action,
760  simulate_cluster_action,
761  simulate_fencing_action,
762  };
763 
764  out = scheduler->priv->out;
765 
766  fake_cib = cib;
767  fake_op_fail_list = op_fail_list;
768 
769  if (!out->is_quiet(out)) {
770  out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
771  }
772 
773  pcmk__set_graph_functions(&simulation_fns);
775  pcmk__log_graph(LOG_DEBUG, transition);
776 
777  fake_resource_list = scheduler->priv->resources;
778  do {
779  graph_rc = pcmk__execute_graph(transition);
780  } while (graph_rc == pcmk__graph_active);
781  fake_resource_list = NULL;
782 
783  if (graph_rc != pcmk__graph_complete) {
784  out->err(out, "Transition failed: %s",
785  pcmk__graph_status2text(graph_rc));
786  pcmk__log_graph(LOG_ERR, transition);
787  out->err(out, "An invalid transition was produced");
788  }
789  pcmk__free_graph(transition);
790 
791  if (!out->is_quiet(out)) {
792  // If not quiet, we'll need the resulting CIB for later display
793  xmlNode *cib_object = NULL;
794  int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
795  cib_sync_call);
796 
797  pcmk__assert(rc == pcmk_ok);
799  scheduler->input = cib_object;
800  out->end_list(out);
801  }
802  return graph_rc;
803 }
804 
805 int
807  const pcmk_injections_t *injections, unsigned int flags,
808  uint32_t section_opts, const char *use_date,
809  const char *input_file, const char *graph_file,
810  const char *dot_file)
811 {
812  int printed = pcmk_rc_no_output;
813  int rc = pcmk_rc_ok;
814  xmlNodePtr input = NULL;
815  cib_t *cib = NULL;
816 
817  rc = cib__signon_query(out, &cib, &input);
818  if (rc != pcmk_rc_ok) {
819  goto simulate_done;
820  }
821 
822  reset(scheduler, input, out, use_date, flags);
824 
825  if (!out->is_quiet(out)) {
826  const bool show_pending = pcmk_is_set(flags, pcmk_sim_show_pending);
827 
829  printed = out->message(out, "maint-mode", scheduler->flags);
830  }
831 
832  if ((scheduler->priv->disabled_resources > 0)
833  || (scheduler->priv->blocked_resources > 0)) {
834 
835  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
836  printed = out->info(out,
837  "%d of %d resource instances DISABLED and "
838  "%d BLOCKED from further action due to failure",
842  }
843 
844  /* Most formatted output headers use caps for each word, but this one
845  * only has the first word capitalized for compatibility with pcs.
846  */
847  print_cluster_status(scheduler, (show_pending? pcmk_show_pending : 0),
848  section_opts, "Current cluster status",
849  (printed == pcmk_rc_ok));
850  printed = pcmk_rc_ok;
851  }
852 
853  // If the user requested any injections, handle them
854  if ((injections->node_down != NULL)
855  || (injections->node_fail != NULL)
856  || (injections->node_up != NULL)
857  || (injections->op_inject != NULL)
858  || (injections->ticket_activate != NULL)
859  || (injections->ticket_grant != NULL)
860  || (injections->ticket_revoke != NULL)
861  || (injections->ticket_standby != NULL)
862  || (injections->watchdog != NULL)) {
863 
864  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
865  pcmk__inject_scheduler_input(scheduler, cib, injections);
866  printed = pcmk_rc_ok;
867 
868  rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
869  if (rc != pcmk_rc_ok) {
870  rc = pcmk_legacy2rc(rc);
871  goto simulate_done;
872  }
873 
875  reset(scheduler, input, out, use_date, flags);
877  }
878 
879  if (input_file != NULL) {
880  rc = pcmk__xml_write_file(input, input_file, false);
881  if (rc != pcmk_rc_ok) {
882  goto simulate_done;
883  }
884  }
885 
886  if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
887  pcmk__output_t *logger_out = NULL;
888  unsigned long long scheduler_flags = pcmk__sched_none;
889 
891  scheduler_flags |= pcmk__sched_output_scores;
892  }
894  scheduler_flags |= pcmk__sched_show_utilization;
895  }
896 
897  if (pcmk_all_flags_set(scheduler->flags,
900  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
901  out->begin_list(out, NULL, NULL,
902  "Assignment Scores and Utilization Information");
903  printed = pcmk_rc_ok;
904 
906  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
907  out->begin_list(out, NULL, NULL, "Assignment Scores");
908  printed = pcmk_rc_ok;
909 
911  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
912  out->begin_list(out, NULL, NULL, "Utilization Information");
913  printed = pcmk_rc_ok;
914 
915  } else {
916  rc = pcmk__log_output_new(&logger_out);
917  if (rc != pcmk_rc_ok) {
918  goto simulate_done;
919  }
920  pe__register_messages(logger_out);
921  pcmk__register_lib_messages(logger_out);
922  scheduler->priv->out = logger_out;
923  }
924 
925  pcmk__schedule_actions(input, scheduler_flags, scheduler);
926 
927  if (logger_out == NULL) {
928  out->end_list(out);
929  } else {
930  logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
931  pcmk__output_free(logger_out);
932  scheduler->priv->out = out;
933  }
934 
935  input = NULL; /* Don't try and free it twice */
936 
937  if (graph_file != NULL) {
938  rc = pcmk__xml_write_file(scheduler->priv->graph, graph_file,
939  false);
940  if (rc != pcmk_rc_ok) {
941  rc = pcmk_rc_graph_error;
942  goto simulate_done;
943  }
944  }
945 
946  if (dot_file != NULL) {
947  rc = write_sim_dotfile(scheduler, dot_file,
950  if (rc != pcmk_rc_ok) {
951  rc = pcmk_rc_dot_error;
952  goto simulate_done;
953  }
954  }
955 
956  if (!out->is_quiet(out)) {
957  print_transition_summary(scheduler, printed == pcmk_rc_ok);
958  }
959  }
960 
961  rc = pcmk_rc_ok;
962 
964  goto simulate_done;
965  }
966 
967  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
968  if (pcmk__simulate_transition(scheduler, cib, injections->op_fail)
971  }
972 
973  if (out->is_quiet(out)) {
974  goto simulate_done;
975  }
976 
977  set_effective_date(scheduler, true, use_date);
978 
981  }
984  }
985 
987  print_cluster_status(scheduler, 0, section_opts, "Revised Cluster Status",
988  true);
989 
990 simulate_done:
992  return rc;
993 }
994 
995 int
997  const pcmk_injections_t *injections, unsigned int flags,
998  unsigned int section_opts, const char *use_date,
999  const char *input_file, const char *graph_file,
1000  const char *dot_file)
1001 {
1002  pcmk__output_t *out = NULL;
1003  int rc = pcmk_rc_ok;
1004 
1005  rc = pcmk__xml_output_new(&out, xml);
1006  if (rc != pcmk_rc_ok) {
1007  return rc;
1008  }
1009 
1010  pe__register_messages(out);
1012 
1013  rc = pcmk__simulate(scheduler, out, injections, flags, section_opts,
1014  use_date, input_file, graph_file, dot_file);
1015  pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
1016  return rc;
1017 }
void(* end_list)(pcmk__output_t *out)
#define PCMK__XA_OPERATION_KEY
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition: xml.c:805
GList * op_inject
Definition: pacemaker.h:61
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition: xml_element.c:42
Actions are ordered if on same node (or migration target for migrate_to)
enum pcmk__graph_status pcmk__simulate_transition(pcmk_scheduler_t *scheduler, cib_t *cib, const GList *op_fail_list)
G_GNUC_INTERNAL void pcmk__inject_failcount(pcmk__output_t *out, cib_t *cib_conn, xmlNode *cib_node, const char *resource, const char *task, guint interval_ms, int rc, bool infinity)
Control output from tools.
#define PCMK__XA_ROUTER_NODE
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:116
#define PCMK_XE_STATUS
Definition: xml_names.h:204
#define crm_time_log_timeofday
Definition: iso8601.h:68
#define PCMK_VALUE_INFINITY
Definition: options.h:164
int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
Definition: cib_utils.c:841
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1027
int(* message)(pcmk__output_t *out, const char *message_id,...)
crm_time_t * pcmk__copy_timet(time_t source)
Definition: iso8601.c:1488
#define PCMK_XE_PRIMITIVE
Definition: xml_names.h:164
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
#define CRM_OP_REPROBE
Definition: crm.h:128
GList * ticket_activate
Definition: pacemaker.h:74
int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
Retrieve the seconds-since-epoch value of an XML attribute.
Definition: xml_element.c:1359
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id)
Definition: status.c:453
char * failed_start_offset
Failcount after one failed start action.
#define PCMK_ACTION_ON
Definition: actions.h:55
G_GNUC_INTERNAL xmlNode * pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up)
G_GNUC_INTERNAL xmlNode * pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op, const char *node, int target_rc)
void pe_reset_working_set(pcmk_scheduler_t *scheduler)
Reset scheduler data to default state without freeing it.
Definition: status.c:393
#define PCMK__META_OP_TARGET_RC
bool(* is_quiet)(pcmk__output_t *out)
#define PCMK_ACTION_META_DATA
Definition: actions.h:47
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition: results.c:810
int pcmk__update_configured_schema(xmlNode **xml, bool to_logs)
Update XML from its configured schema to the latest major series.
Definition: schemas.c:1159
GList * node_up
Definition: pacemaker.h:52
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
#define PCMK_XA_PROVIDER
Definition: xml_names.h:364
G_GNUC_INTERNAL xmlNode * pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid)
High Level API.
uint64_t flags
Definition: scheduler.h:89
#define PCMK__META_STONITH_ACTION
void pcmk__schedule_actions(xmlNode *cib, unsigned long long flags, pcmk_scheduler_t *scheduler)
char * crm_system_name
Definition: utils.c:44
enum crm_exit_e crm_exit_t
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition: output.c:271
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:196
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
GList * ticket_grant
Definition: pacemaker.h:68
#define PCMK__META_ON_NODE
enum ocf_exitcode rc
Definition: lrmd_events.h:65
#define PCMK_XA_TYPE
Definition: xml_names.h:430
gboolean cluster_status(pcmk_scheduler_t *scheduler)
Definition: status.c:103
#define PCMK_XA_OPERATION
Definition: xml_names.h:349
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
Definition: xml_element.c:407
const char * action
Definition: pcmk_fence.c:32
#define PCMK__XE_TRANSIENT_ATTRIBUTES
Scheduler API.
void pcmk__xml_free(xmlNode *xml)
Definition: xml.c:789
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: xml_element.c:1015
#define PCMK__OUTPUT_SPACER_IF(out_obj, cond)
char * crm_time_as_string(const crm_time_t *dt, int flags)
Get a string representation of a crm_time_t object.
Definition: iso8601.c:714
enum pcmk__graph_status pcmk__execute_graph(pcmk__graph_t *graph)
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition: strings.c:303
char * failed_stop_offset
Failcount after one failed stop action.
cib_api_operations_t * cmds
Definition: cib_types.h:325
#define crm_debug(fmt, args...)
Definition: logging.h:370
int pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out, const pcmk_injections_t *injections, unsigned int flags, uint32_t section_opts, const char *use_date, const char *input_file, const char *graph_file, const char *dot_file)
#define PCMK_XA_UNAME
Definition: xml_names.h:431
pcmk__graph_status
void pcmk__set_graph_functions(pcmk__graph_functions_t *fns)
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
int pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress)
Definition: xml_io.c:601
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: xml_element.c:1466
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: xml_element.c:1168
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:244
GList * ticket_standby
Definition: pacemaker.h:72
lrmd_event_data_t * pcmk__event_from_graph_action(const xmlNode *resource, const pcmk__graph_action_t *action, int status, int rc, const char *exit_reason)
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:1297
bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context)
Definition: schemas.c:728
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
int(* modify)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:169
bool pcmk_str_is_infinity(const char *s)
Definition: scores.c:130
int(*) int(*) void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void pcmk__update_graph(pcmk__graph_t *graph, const pcmk__graph_action_t *action)
#define PCMK_ACTION_START
Definition: actions.h:63
#define PCMK__META_ON_NODE_UUID
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:2709
void pcmk__free_graph(pcmk__graph_t *graph)
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
#define PCMK__VALUE_BOLD
Action completed, result is known.
Definition: results.h:309
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition: cib_types.h:151
#define PCMK_ACTION_STOP
Definition: actions.h:66
#define PCMK_ACTION_STONITH
Definition: actions.h:65
#define PCMK_ACTION_NOTIFIED
Definition: actions.h:52
#define crm_time_log(level, prefix, dt, flags)
Definition: iso8601.h:60
#define PCMK_XA_ID
Definition: xml_names.h:301
Success.
Definition: results.h:231
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:667
G_GNUC_INTERNAL void pcmk__output_actions(pcmk_scheduler_t *scheduler)
#define PCMK__XE_LRM
int(* replace)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:172
void pe__register_messages(pcmk__output_t *out)
Definition: pe_output.c:3470
#define PCMK_ACTION_DELETE
Definition: actions.h:39
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: actions.c:195
int cib__clean_up_connection(cib_t **cib)
Definition: cib_utils.c:920
Function and executable result codes.
pcmk__graph_t * pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference)
const char * op_type
Definition: lrmd_events.h:45
#define PCMK_ACTION_CANCEL
Definition: actions.h:36
void cleanup_calculations(pcmk_scheduler_t *scheduler)
Reset scheduler data to defaults without freeing it or constraints.
Definition: status.c:338
void pcmk__output_free(pcmk__output_t *out)
Definition: output.c:30
xmlNode * pcmk__xml_read(const char *filename)
Definition: xml_io.c:102
int pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler, const pcmk_injections_t *injections, unsigned int flags, unsigned int section_opts, const char *use_date, const char *input_file, const char *graph_file, const char *dot_file)
Simulate a cluster&#39;s response to events.
#define pcmk__assert(expr)
#define PCMK__XA_LONG_ID
const char * target
Definition: pcmk_fence.c:31
pcmk_pacemakerd_state
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: actions.c:335
#define crm_log_xml_err(xml, text)
Definition: logging.h:375
#define PCMK_XA_CLASS
Definition: xml_names.h:246
#define PCMK_META_INTERVAL
Definition: options.h:91
GList * ticket_revoke
Definition: pacemaker.h:70
#define crm_err(fmt, args...)
Definition: logging.h:359
pcmk_scheduler_t * scheduler
#define pcmk__set_graph_action_flags(action, flags_to_set)
Success.
Definition: results.h:174
xmlNode * input
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:112
Synthetic cluster events that can be injected into the cluster for running simulations.
Definition: pacemaker.h:50
xmlNode * input
Definition: scheduler.h:81
G_GNUC_INTERNAL void pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, const pcmk_injections_t *injections)
void lrmd_free_event(lrmd_event_data_t *event)
Free an executor event.
Definition: lrmd_client.c:246
GList * node_fail
Definition: pacemaker.h:56
This structure contains everything that makes up a single output formatter.
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
const char * pcmk__graph_status2text(enum pcmk__graph_status state)
#define PCMK__XE_NODE_STATE
#define PCMK_XA_EXECUTION_DATE
Definition: xml_names.h:272
#define pcmk_ok
Definition: results.h:65
#define pcmk__set_action_flags(action, flags_to_set)
GList * op_fail
Definition: pacemaker.h:66
void pcmk__profile_dir(const char *dir, long long repeat, pcmk_scheduler_t *scheduler, const char *use_date)
void pcmk__log_graph(unsigned int log_level, pcmk__graph_t *graph)
int(* remove)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:174
G_GNUC_INTERNAL xmlNode * pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, const char *resource, const char *lrm_name, const char *rclass, const char *rtype, const char *rprovider)
const char * crm_meta_value(GHashTable *hash, const char *field)
Get the value of a meta-attribute.
Definition: nvpair.c:435
#define PCMK_XA_CRM_DEBUG_ORIGIN
Definition: xml_names.h:253
#define pcmk__set_scheduler_flags(scheduler, flags_to_set)
#define crm_time_log_date
Definition: iso8601.h:67
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:637
GList * node_down
Definition: pacemaker.h:54
uint64_t flags
Definition: remote.c:211
int pcmk__log_output_new(pcmk__output_t **out)
Definition: output.c:291
#define PCMK_ACTION_NOTIFY
Definition: actions.h:53
No relation (compare with equality rather than bit set)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:26
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1