pacemaker  2.1.6-802a72226b
Scalable High-Availability cluster resource manager
pcmk_simulate.c
Go to the documentation of this file.
1 /*
2  * Copyright 2021-2023 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU Lesser General Public License
7  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 #include <crm/cib/internal.h>
12 #include <crm/common/output.h>
13 #include <crm/common/results.h>
14 #include <crm/pengine/pe_types.h>
15 #include <pacemaker-internal.h>
16 #include <pacemaker.h>
17 
18 #include <stdint.h>
19 #include <sys/types.h>
20 #include <sys/stat.h>
21 #include <unistd.h>
22 
23 #include "libpacemaker_private.h"
24 
25 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(pe_working_set_t *data_set, bool print_original,
31  const char *use_date);
32 
43 static char *
44 create_action_name(const pe_action_t *action, bool verbose)
45 {
46  char *action_name = NULL;
47  const char *prefix = "";
48  const char *action_host = NULL;
49  const char *clone_name = NULL;
50  const char *task = action->task;
51 
52  if (action->node != NULL) {
53  action_host = action->node->details->uname;
54  } else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
55  action_host = "<none>";
56  }
57 
58  if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_none)) {
59  prefix = "Cancel ";
60  task = action->cancel_task;
61  }
62 
63  if (action->rsc != NULL) {
64  clone_name = action->rsc->clone_name;
65  }
66 
67  if (clone_name != NULL) {
68  char *key = NULL;
69  guint interval_ms = 0;
70 
71  if (pcmk__guint_from_hash(action->meta,
73  &interval_ms) != pcmk_rc_ok) {
74  interval_ms = 0;
75  }
76 
78  NULL)) {
79  const char *n_type = g_hash_table_lookup(action->meta,
80  "notify_key_type");
81  const char *n_task = g_hash_table_lookup(action->meta,
82  "notify_key_operation");
83 
84  CRM_ASSERT(n_type != NULL);
85  CRM_ASSERT(n_task != NULL);
86  key = pcmk__notify_key(clone_name, n_type, n_task);
87  } else {
88  key = pcmk__op_key(clone_name, task, interval_ms);
89  }
90 
91  if (action_host != NULL) {
92  action_name = crm_strdup_printf("%s%s %s",
93  prefix, key, action_host);
94  } else {
95  action_name = crm_strdup_printf("%s%s", prefix, key);
96  }
97  free(key);
98 
99  } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
100  const char *op = g_hash_table_lookup(action->meta, "stonith_action");
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(pe_working_set_t *data_set, uint32_t show_opts,
138  uint32_t section_opts, const char *title, bool print_spacer)
139 {
140  pcmk__output_t *out = data_set->priv;
141  GList *all = NULL;
142  crm_exit_t stonith_rc = 0;
144 
145  section_opts |= pcmk_section_nodes | pcmk_section_resources;
147 
148  all = g_list_prepend(all, (gpointer) "*");
149 
150  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
151  out->begin_list(out, NULL, NULL, "%s", title);
152  out->message(out, "cluster-status",
153  data_set, state, stonith_rc, NULL,
154  false, section_opts, show_opts, NULL, all, all);
155  out->end_list(out);
156 
157  g_list_free(all);
158 }
159 
167 static void
168 print_transition_summary(pe_working_set_t *data_set, bool print_spacer)
169 {
170  pcmk__output_t *out = data_set->priv;
171 
172  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
173  out->begin_list(out, NULL, NULL, "Transition Summary");
175  out->end_list(out);
176 }
177 
188 static void
189 reset(pe_working_set_t *data_set, xmlNodePtr input, pcmk__output_t *out,
190  const char *use_date, unsigned int flags)
191 {
192  data_set->input = input;
193  data_set->priv = out;
194  set_effective_date(data_set, true, use_date);
197  }
200  }
203  }
204 }
205 
219 static int
220 write_sim_dotfile(pe_working_set_t *data_set, const char *dot_file,
221  bool all_actions, bool verbose)
222 {
223  GList *gIter = NULL;
224  FILE *dot_strm = fopen(dot_file, "w");
225 
226  if (dot_strm == NULL) {
227  return errno;
228  }
229 
230  fprintf(dot_strm, " digraph \"g\" {\n");
231  for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
232  pe_action_t *action = (pe_action_t *) gIter->data;
233  const char *style = "dashed";
234  const char *font = "black";
235  const char *color = "black";
236  char *action_name = create_action_name(action, verbose);
237 
238  if (pcmk_is_set(action->flags, pe_action_pseudo)) {
239  font = "orange";
240  }
241 
242  if (pcmk_is_set(action->flags, pe_action_dumped)) {
243  style = "bold";
244  color = "green";
245 
246  } else if ((action->rsc != NULL)
247  && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
248  color = "red";
249  font = "purple";
250  if (!all_actions) {
251  goto do_not_write;
252  }
253 
254  } else if (pcmk_is_set(action->flags, pe_action_optional)) {
255  color = "blue";
256  if (!all_actions) {
257  goto do_not_write;
258  }
259 
260  } else {
261  color = "red";
263  }
264 
266  fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
267  action_name, style, color, font);
268  do_not_write:
269  free(action_name);
270  }
271 
272  for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
273  pe_action_t *action = (pe_action_t *) gIter->data;
274 
275  GList *gIter2 = NULL;
276 
277  for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
278  pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
279 
280  char *before_name = NULL;
281  char *after_name = NULL;
282  const char *style = "dashed";
283  bool optional = true;
284 
285  if (before->state == pe_link_dumped) {
286  optional = false;
287  style = "bold";
288  } else if (before->type == pe_order_none) {
289  continue;
290  } else if (pcmk_is_set(before->action->flags, pe_action_dumped)
292  && before->type != pe_order_load) {
293  optional = false;
294  }
295 
296  if (all_actions || !optional) {
297  before_name = create_action_name(before->action, verbose);
298  after_name = create_action_name(action, verbose);
299  fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
300  before_name, after_name, style);
301  free(before_name);
302  free(after_name);
303  }
304  }
305  }
306 
307  fprintf(dot_strm, "}\n");
308  fflush(dot_strm);
309  fclose(dot_strm);
310  return pcmk_rc_ok;
311 }
312 
325 static void
326 profile_file(const char *xml_file, long long repeat, pe_working_set_t *data_set,
327  const char *use_date)
328 {
329  pcmk__output_t *out = data_set->priv;
330  xmlNode *cib_object = NULL;
331  clock_t start = 0;
332  clock_t end;
333  unsigned long long data_set_flags = pe_flag_no_compat;
334 
335  CRM_ASSERT(out != NULL);
336 
337  cib_object = filename2xml(xml_file);
338  start = clock();
339 
340  if (pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS) == NULL) {
341  create_xml_node(cib_object, XML_CIB_TAG_STATUS);
342  }
343 
344  if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
345  free_xml(cib_object);
346  return;
347  }
348 
349  if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
350  free_xml(cib_object);
351  return;
352  }
353 
355  data_set_flags |= pe_flag_show_scores;
356  }
358  data_set_flags |= pe_flag_show_utilization;
359  }
360 
361  for (int i = 0; i < repeat; ++i) {
362  xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
363 
364  data_set->input = input;
365  set_effective_date(data_set, false, use_date);
366  pcmk__schedule_actions(input, data_set_flags, data_set);
368  }
369 
370  end = clock();
371  out->message(out, "profile", xml_file, start, end);
372 }
373 
374 void
375 pcmk__profile_dir(const char *dir, long long repeat, pe_working_set_t *data_set,
376  const char *use_date)
377 {
378  pcmk__output_t *out = data_set->priv;
379  struct dirent **namelist;
380 
381  int file_num = scandir(dir, &namelist, 0, alphasort);
382 
383  CRM_ASSERT(out != NULL);
384 
385  if (file_num > 0) {
386  struct stat prop;
387  char buffer[FILENAME_MAX];
388 
389  out->begin_list(out, NULL, NULL, "Timings");
390 
391  while (file_num--) {
392  if ('.' == namelist[file_num]->d_name[0]) {
393  free(namelist[file_num]);
394  continue;
395 
396  } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
397  ".xml")) {
398  free(namelist[file_num]);
399  continue;
400  }
401  snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
402  if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
403  profile_file(buffer, repeat, data_set, use_date);
404  }
405  free(namelist[file_num]);
406  }
407  free(namelist);
408 
409  out->end_list(out);
410  }
411 }
412 
426 static void
427 set_effective_date(pe_working_set_t *data_set, bool print_original,
428  const char *use_date)
429 {
430  pcmk__output_t *out = data_set->priv;
431  time_t original_date = 0;
432 
433  CRM_ASSERT(out != NULL);
434 
435  crm_element_value_epoch(data_set->input, "execution-date", &original_date);
436 
437  if (use_date) {
438  data_set->now = crm_time_new(use_date);
439  out->info(out, "Setting effective cluster time: %s", use_date);
440  crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
442 
443  } else if (original_date != 0) {
444  data_set->now = pcmk__copy_timet(original_date);
445 
446  if (print_original) {
447  char *when = crm_time_as_string(data_set->now,
449 
450  out->info(out, "Using the original execution date of: %s", when);
451  free(when);
452  }
453  }
454 }
455 
465 static int
466 simulate_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
467 {
468  const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
469  const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
470 
472  out->message(out, "inject-pseudo-action", node, task);
473 
474  pcmk__update_graph(graph, action);
475  return pcmk_rc_ok;
476 }
477 
487 static int
488 simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
489 {
490  int rc;
491  lrmd_event_data_t *op = NULL;
492  int target_outcome = PCMK_OCF_OK;
493 
494  const char *rtype = NULL;
495  const char *rclass = NULL;
496  const char *resource = NULL;
497  const char *rprovider = NULL;
498  const char *resource_config_name = NULL;
499  const char *operation = crm_element_value(action->xml, "operation");
500  const char *target_rc_s = crm_meta_value(action->params,
502 
503  xmlNode *cib_node = NULL;
504  xmlNode *cib_resource = NULL;
505  xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
506 
508  char *uuid = NULL;
509  const char *router_node = crm_element_value(action->xml,
511 
512  // Certain actions don't need to be displayed or history entries
513  if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) {
514  crm_debug("No history injection for %s op on %s", operation, node);
515  goto done; // Confirm action and update graph
516  }
517 
518  if (action_rsc == NULL) { // Shouldn't be possible
519  crm_log_xml_err(action->xml, "Bad");
520  free(node);
521  return EPROTO;
522  }
523 
524  /* A resource might be known by different names in the configuration and in
525  * the action (for example, a clone instance). Grab the configuration name
526  * (which is preferred when writing history), and if necessary, the instance
527  * name.
528  */
529  resource_config_name = crm_element_value(action_rsc, XML_ATTR_ID);
530  if (resource_config_name == NULL) { // Shouldn't be possible
531  crm_log_xml_err(action->xml, "No ID");
532  free(node);
533  return EPROTO;
534  }
535  resource = resource_config_name;
536  if (pe_find_resource(fake_resource_list, resource) == NULL) {
537  const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG);
538 
539  if ((longname != NULL)
540  && (pe_find_resource(fake_resource_list, longname) != NULL)) {
541  resource = longname;
542  }
543  }
544 
545  // Certain actions need to be displayed but don't need history entries
546  if (pcmk__strcase_any_of(operation, "delete", RSC_METADATA, NULL)) {
547  out->message(out, "inject-rsc-action", resource, operation, node,
548  (guint) 0);
549  goto done; // Confirm action and update graph
550  }
551 
552  rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS);
553  rtype = crm_element_value(action_rsc, XML_ATTR_TYPE);
554  rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER);
555 
556  pcmk__scan_min_int(target_rc_s, &target_outcome, 0);
557 
558  CRM_ASSERT(fake_cib->cmds->query(fake_cib, NULL, NULL,
560 
561  // Ensure the action node is in the CIB
563  cib_node = pcmk__inject_node(fake_cib, node,
564  ((router_node == NULL)? uuid: node));
565  free(uuid);
566  CRM_ASSERT(cib_node != NULL);
567 
568  // Add a history entry for the action
569  cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
570  resource_config_name,
571  rclass, rtype, rprovider);
572  if (cib_resource == NULL) {
573  crm_err("Could not simulate action %d history for resource %s",
574  action->id, resource);
575  free(node);
576  free_xml(cib_node);
577  return EINVAL;
578  }
579 
580  // Simulate and display an executor event for the action result
582  target_outcome, "User-injected result");
583  out->message(out, "inject-rsc-action", resource, op->op_type, node,
584  op->interval_ms);
585 
586  // Check whether action is in a list of desired simulated failures
587  for (const GList *iter = fake_op_fail_list;
588  iter != NULL; iter = iter->next) {
589  const char *spec = (const char *) iter->data;
590  char *key = NULL;
591  const char *match_name = NULL;
592 
593  // Allow user to specify anonymous clone with or without instance number
594  key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
595  op->interval_ms, node);
596  if (strncasecmp(key, spec, strlen(key)) == 0) {
597  match_name = resource;
598  }
599  free(key);
600 
601  // If not found, try the resource's name in the configuration
602  if ((match_name == NULL)
603  && (strcmp(resource, resource_config_name) != 0)) {
604 
605  key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
606  op->op_type, op->interval_ms, node);
607  if (strncasecmp(key, spec, strlen(key)) == 0) {
608  match_name = resource_config_name;
609  }
610  free(key);
611  }
612 
613  if (match_name == NULL) {
614  continue; // This failed action entry doesn't match
615  }
616 
617  // ${match_name}_${task}_${interval_in_ms}@${node}=${rc}
618  rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
619  if (rc != 1) {
620  out->err(out, "Invalid failed operation '%s' "
621  "(result code must be integer)", spec);
622  continue; // Keep checking other list entries
623  }
624 
625  out->info(out, "Pretending action %d failed with rc=%d",
626  action->id, op->rc);
628  graph->abort_priority = INFINITY;
629  pcmk__inject_failcount(out, cib_node, match_name, op->op_type,
630  op->interval_ms, op->rc);
631  break;
632  }
633 
634  pcmk__inject_action_result(cib_resource, op, target_outcome);
635  lrmd_free_event(op);
636  rc = fake_cib->cmds->modify(fake_cib, XML_CIB_TAG_STATUS, cib_node,
638  CRM_ASSERT(rc == pcmk_ok);
639 
640  done:
641  free(node);
642  free_xml(cib_node);
644  pcmk__update_graph(graph, action);
645  return pcmk_rc_ok;
646 }
647 
657 static int
658 simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
659 {
660  const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
661  const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
662  xmlNode *rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
663 
665  out->message(out, "inject-cluster-action", node, task, rsc);
666  pcmk__update_graph(graph, action);
667  return pcmk_rc_ok;
668 }
669 
679 static int
680 simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
681 {
682  const char *op = crm_meta_value(action->params, "stonith_action");
684 
685  out->message(out, "inject-fencing-action", target, op);
686 
687  if (!pcmk__str_eq(op, "on", pcmk__str_casei)) {
688  int rc = pcmk_ok;
689  GString *xpath = g_string_sized_new(512);
690 
691  // Set node state to offline
692  xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
693  false);
694 
695  CRM_ASSERT(cib_node != NULL);
696  crm_xml_add(cib_node, XML_ATTR_ORIGIN, __func__);
697  rc = fake_cib->cmds->replace(fake_cib, XML_CIB_TAG_STATUS, cib_node,
699  CRM_ASSERT(rc == pcmk_ok);
700 
701  // Simulate controller clearing node's resource history and attributes
702  pcmk__g_strcat(xpath,
703  "//" XML_CIB_TAG_STATE
704  "[@" XML_ATTR_UNAME "='", target, "']/" XML_CIB_TAG_LRM,
705  NULL);
706  fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
708 
709  g_string_truncate(xpath, 0);
710  pcmk__g_strcat(xpath,
711  "//" XML_CIB_TAG_STATE
712  "[@" XML_ATTR_UNAME "='", target, "']"
713  "/" XML_TAG_TRANSIENT_NODEATTRS, NULL);
714  fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
716 
717  free_xml(cib_node);
718  g_string_free(xpath, TRUE);
719  }
720 
722  pcmk__update_graph(graph, action);
723  free(target);
724  return pcmk_rc_ok;
725 }
726 
729  const GList *op_fail_list)
730 {
731  pcmk__graph_t *transition = NULL;
732  enum pcmk__graph_status graph_rc;
733 
734  pcmk__graph_functions_t simulation_fns = {
735  simulate_pseudo_action,
736  simulate_resource_action,
737  simulate_cluster_action,
738  simulate_fencing_action,
739  };
740 
741  out = data_set->priv;
742 
743  fake_cib = cib;
744  fake_op_fail_list = op_fail_list;
745 
746  if (!out->is_quiet(out)) {
747  out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
748  }
749 
750  pcmk__set_graph_functions(&simulation_fns);
752  pcmk__log_graph(LOG_DEBUG, transition);
753 
754  fake_resource_list = data_set->resources;
755  do {
756  graph_rc = pcmk__execute_graph(transition);
757  } while (graph_rc == pcmk__graph_active);
758  fake_resource_list = NULL;
759 
760  if (graph_rc != pcmk__graph_complete) {
761  out->err(out, "Transition failed: %s",
762  pcmk__graph_status2text(graph_rc));
763  pcmk__log_graph(LOG_ERR, transition);
764  out->err(out, "An invalid transition was produced");
765  }
766  pcmk__free_graph(transition);
767 
768  if (!out->is_quiet(out)) {
769  // If not quiet, we'll need the resulting CIB for later display
770  xmlNode *cib_object = NULL;
771  int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
773 
774  CRM_ASSERT(rc == pcmk_ok);
776  data_set->input = cib_object;
777  out->end_list(out);
778  }
779  return graph_rc;
780 }
781 
782 int
784  const pcmk_injections_t *injections, unsigned int flags,
785  uint32_t section_opts, const char *use_date,
786  const char *input_file, const char *graph_file,
787  const char *dot_file)
788 {
789  int printed = pcmk_rc_no_output;
790  int rc = pcmk_rc_ok;
791  xmlNodePtr input = NULL;
792  cib_t *cib = NULL;
793 
794  rc = cib__signon_query(out, &cib, &input);
795  if (rc != pcmk_rc_ok) {
796  goto simulate_done;
797  }
798 
799  reset(data_set, input, out, use_date, flags);
801 
802  if ((cib->variant == cib_native)
803  && pcmk_is_set(section_opts, pcmk_section_times)) {
804  if (pcmk__our_nodename == NULL) {
805  // Currently used only in the times section
806  pcmk__query_node_name(out, 0, &pcmk__our_nodename, 0);
807  }
809  }
810 
811  if (!out->is_quiet(out)) {
813  printed = out->message(out, "maint-mode", data_set->flags);
814  }
815 
817  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
818  printed = out->info(out,
819  "%d of %d resource instances DISABLED and "
820  "%d BLOCKED from further action due to failure",
824  }
825 
826  /* Most formatted output headers use caps for each word, but this one
827  * only has the first word capitalized for compatibility with pcs.
828  */
829  print_cluster_status(data_set,
831  section_opts, "Current cluster status",
832  (printed == pcmk_rc_ok));
833  printed = pcmk_rc_ok;
834  }
835 
836  // If the user requested any injections, handle them
837  if ((injections->node_down != NULL)
838  || (injections->node_fail != NULL)
839  || (injections->node_up != NULL)
840  || (injections->op_inject != NULL)
841  || (injections->ticket_activate != NULL)
842  || (injections->ticket_grant != NULL)
843  || (injections->ticket_revoke != NULL)
844  || (injections->ticket_standby != NULL)
845  || (injections->watchdog != NULL)) {
846 
847  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
848  pcmk__inject_scheduler_input(data_set, cib, injections);
849  printed = pcmk_rc_ok;
850 
851  rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
852  if (rc != pcmk_rc_ok) {
853  rc = pcmk_legacy2rc(rc);
854  goto simulate_done;
855  }
856 
858  reset(data_set, input, out, use_date, flags);
860  }
861 
862  if (input_file != NULL) {
863  rc = write_xml_file(input, input_file, FALSE);
864  if (rc < 0) {
865  rc = pcmk_legacy2rc(rc);
866  goto simulate_done;
867  }
868  }
869 
870  if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
871  pcmk__output_t *logger_out = NULL;
872  unsigned long long data_set_flags = pe_flag_no_compat;
873 
875  data_set_flags |= pe_flag_show_scores;
876  }
878  data_set_flags |= pe_flag_show_utilization;
879  }
880 
881  if (pcmk_all_flags_set(data_set->flags,
883  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
884  out->begin_list(out, NULL, NULL,
885  "Allocation Scores and Utilization Information");
886  printed = pcmk_rc_ok;
887 
889  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
890  out->begin_list(out, NULL, NULL, "Allocation Scores");
891  printed = pcmk_rc_ok;
892 
894  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
895  out->begin_list(out, NULL, NULL, "Utilization Information");
896  printed = pcmk_rc_ok;
897 
898  } else {
899  rc = pcmk__log_output_new(&logger_out);
900  if (rc != pcmk_rc_ok) {
901  goto simulate_done;
902  }
903  pe__register_messages(logger_out);
904  pcmk__register_lib_messages(logger_out);
905  data_set->priv = logger_out;
906  }
907 
908  pcmk__schedule_actions(input, data_set_flags, data_set);
909 
910  if (logger_out == NULL) {
911  out->end_list(out);
912  } else {
913  logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
914  pcmk__output_free(logger_out);
915  data_set->priv = out;
916  }
917 
918  input = NULL; /* Don't try and free it twice */
919 
920  if (graph_file != NULL) {
921  rc = write_xml_file(data_set->graph, graph_file, FALSE);
922  if (rc < 0) {
923  rc = pcmk_rc_graph_error;
924  goto simulate_done;
925  }
926  }
927 
928  if (dot_file != NULL) {
929  rc = write_sim_dotfile(data_set, dot_file,
932  if (rc != pcmk_rc_ok) {
933  rc = pcmk_rc_dot_error;
934  goto simulate_done;
935  }
936  }
937 
938  if (!out->is_quiet(out)) {
939  print_transition_summary(data_set, printed == pcmk_rc_ok);
940  }
941  }
942 
943  rc = pcmk_rc_ok;
944 
946  goto simulate_done;
947  }
948 
949  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
951  injections->op_fail) != pcmk__graph_complete) {
953  }
954 
955  if (out->is_quiet(out)) {
956  goto simulate_done;
957  }
958 
959  set_effective_date(data_set, true, use_date);
960 
963  }
966  }
967 
969  print_cluster_status(data_set, 0, section_opts, "Revised Cluster Status",
970  true);
971 
972 simulate_done:
974  return rc;
975 }
976 
977 int
979  const pcmk_injections_t *injections, unsigned int flags,
980  unsigned int section_opts, const char *use_date,
981  const char *input_file, const char *graph_file,
982  const char *dot_file)
983 {
984  pcmk__output_t *out = NULL;
985  int rc = pcmk_rc_ok;
986 
987  rc = pcmk__xml_output_new(&out, xml);
988  if (rc != pcmk_rc_ok) {
989  return rc;
990  }
991 
994 
995  rc = pcmk__simulate(data_set, out, injections, flags, section_opts,
996  use_date, input_file, graph_file, dot_file);
997  pcmk__xml_output_finish(out, xml);
998  return rc;
999 }
#define XML_ATTR_ID_LONG
Definition: msg_xml.h:150
void(* end_list)(pcmk__output_t *out)
enum pe_link_state state
Definition: pe_types.h:556
int pcmk_simulate(xmlNodePtr *xml, pe_working_set_t *data_set, 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.
GList * op_inject
Definition: pacemaker.h:61
int pcmk__simulate(pe_working_set_t *data_set, 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 INFINITY
Definition: crm.h:99
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:89
Control output from tools.
#define CRM_OP_FENCE
Definition: crm.h:144
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
#define crm_time_log_timeofday
Definition: iso8601.h:68
int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
Definition: cib_utils.c:745
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:933
int(* message)(pcmk__output_t *out, const char *message_id,...)
#define XML_ATTR_TYPE
Definition: msg_xml.h:151
crm_time_t * pcmk__copy_timet(time_t source)
Definition: iso8601.c:1391
#define RSC_NOTIFIED
Definition: crm.h:211
#define CRM_OP_REPROBE
Definition: crm.h:152
GList * ticket_activate
Definition: pacemaker.h:74
Internal tracking for transition graph creation.
Definition: pe_types.h:495
#define pe_flag_no_compat
Definition: pe_types.h:148
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2521
G_GNUC_INTERNAL xmlNode * pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up)
#define pe_flag_maintenance_mode
Definition: pe_types.h:113
bool(* is_quiet)(pcmk__output_t *out)
G_GNUC_INTERNAL void pcmk__inject_failcount(pcmk__output_t *out, xmlNode *cib_node, const char *resource, const char *task, guint interval_ms, int rc)
#define XML_TAG_TRANSIENT_NODEATTRS
Definition: msg_xml.h:429
int alphasort(const void *dirent1, const void *dirent2)
const char * crm_meta_value(GHashTable *hash, const char *field)
Definition: utils.c:490
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
Definition: nvpair.c:302
GList * node_up
Definition: pacemaker.h:52
void lrmd_free_event(lrmd_event_data_t *event)
Free an executor event.
Definition: lrmd_client.c:243
int write_xml_file(xmlNode *xml_node, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1265
G_GNUC_INTERNAL xmlNode * pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid)
High Level API.
char * crm_system_name
Definition: utils.c:51
enum crm_exit_e crm_exit_t
pe_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition: status.c:391
GList * actions
Definition: pe_types.h:187
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:219
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
GList * ticket_grant
Definition: pacemaker.h:68
#define XML_CIB_TAG_LRM
Definition: msg_xml.h:278
enum ocf_exitcode rc
Definition: lrmd.h:239
xmlNode * filename2xml(const char *filename)
Definition: xml.c:1007
pe_action_t * action
Definition: pe_types.h:557
#define RSC_NOTIFY
Definition: crm.h:210
const char * action
Definition: pcmk_fence.c:30
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition: schemas.c:707
GList * resources
Definition: pe_types.h:181
#define XML_ATTR_ORIGIN
Definition: msg_xml.h:142
#define PCMK__OUTPUT_SPACER_IF(out_obj, cond)
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:316
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:819
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:315
void pcmk__profile_dir(const char *dir, long long repeat, pe_working_set_t *data_set, const char *use_date)
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:694
void pe_reset_working_set(pe_working_set_t *data_set)
Reset a working set to default state without freeing it.
Definition: status.c:338
void cleanup_calculations(pe_working_set_t *data_set)
Reset working set to default state without freeing it or constraints.
Definition: status.c:279
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: operations.c:183
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:311
cib_api_operations_t * cmds
Definition: cib_types.h:216
#define crm_debug(fmt, args...)
Definition: logging.h:382
#define pe_flag_sanitized
Definition: pe_types.h:137
pcmk__graph_status
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:693
#define XML_ATTR_ID
Definition: msg_xml.h:147
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:496
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:230
void pcmk__set_graph_functions(pcmk__graph_functions_t *fns)
#define XML_CIB_TAG_STATE
Definition: msg_xml.h:217
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:153
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition: output.c:236
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:1217
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:121
int blocked_resources
Definition: pe_types.h:205
int(* modify)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:127
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 XML_AGENT_ATTR_PROVIDER
Definition: msg_xml.h:283
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:2329
enum pcmk__graph_status pcmk__simulate_transition(pe_working_set_t *data_set, cib_t *cib, const GList *op_fail_list)
void pcmk__free_graph(pcmk__graph_t *graph)
pe_working_set_t * data_set
#define XML_ATTR_UNAME
Definition: msg_xml.h:170
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:677
Action completed, result is known.
Definition: results.h:315
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition: cib_types.h:103
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: nvpair.c:617
#define crm_time_log(level, prefix, dt, flags)
Definition: iso8601.h:60
Success.
Definition: results.h:237
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:546
G_GNUC_INTERNAL void pcmk__output_actions(pe_working_set_t *data_set)
int(* replace)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:134
void pe__register_messages(pcmk__output_t *out)
Definition: pe_output.c:3106
#define XML_LRM_ATTR_ROUTER_NODE
Definition: msg_xml.h:323
void free_xml(xmlNode *child)
Definition: xml.c:813
xmlNode * input
Definition: pe_types.h:160
int cib__clean_up_connection(cib_t **cib)
Definition: cib_utils.c:799
Function and executable result codes.
pcmk__graph_t * pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference)
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: operations.c:42
const char * op_type
Definition: lrmd.h:223
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:318
void pcmk__output_free(pcmk__output_t *out)
Definition: output.c:28
const char * target
Definition: pcmk_fence.c:29
pcmk_pacemakerd_state
const char * localhost
Definition: pe_types.h:202
#define crm_log_xml_err(xml, text)
Definition: logging.h:386
#define pe_flag_show_utilization
Definition: pe_types.h:151
GList * ticket_revoke
Definition: pacemaker.h:70
#define crm_err(fmt, args...)
Definition: logging.h:377
char * pcmk__our_nodename
Node name of the local node.
Definition: logging.c:48
#define CRM_ASSERT(expr)
Definition: results.h:42
#define pcmk__set_graph_action_flags(action, flags_to_set)
Success.
Definition: results.h:167
int disabled_resources
Definition: pe_types.h:206
gboolean cluster_status(pe_working_set_t *data_set)
Definition: status.c:71
xmlNode * input
crm_time_t * crm_time_new(const char *string)
Definition: iso8601.c:109
Synthetic cluster events that can be injected into the cluster for running simulations.
Definition: pacemaker.h:50
GList * node_fail
Definition: pacemaker.h:56
This structure contains everything that makes up a single output formatter.
#define XML_LRM_ATTR_INTERVAL_MS
Definition: msg_xml.h:313
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
enum cib_variant variant
Definition: cib_types.h:206
const char * pcmk__graph_status2text(enum pcmk__graph_status state)
#define pe__set_working_set_flags(working_set, flags_to_set)
Definition: internal.h:65
enum pe_action_flags flags
Definition: pe_types.h:442
#define pcmk_ok
Definition: results.h:68
GList * op_fail
Definition: pacemaker.h:66
void pcmk__xml_output_finish(pcmk__output_t *out, xmlNodePtr *xml)
Definition: output.c:258
void pcmk__log_graph(unsigned int log_level, pcmk__graph_t *graph)
#define XML_CIB_TAG_STATUS
Definition: msg_xml.h:198
G_GNUC_INTERNAL xmlNode * pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op, int target_rc)
int(* remove)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:136
#define RSC_METADATA
Definition: crm.h:214
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)
Data types for cluster status.
#define RSC_CANCEL
Definition: crm.h:194
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:317
unsigned long long flags
Definition: pe_types.h:169
void pcmk__inject_scheduler_input(pe_working_set_t *data_set, cib_t *cib, const pcmk_injections_t *injections)
#define PCMK__OP_FMT
Definition: internal.h:170
gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
Definition: schemas.c:1197
enum pe_ordering type
Definition: pe_types.h:555
#define pe_flag_show_scores
Definition: pe_types.h:150
crm_time_t * now
Definition: pe_types.h:161
void pcmk__schedule_actions(xmlNode *cib, unsigned long long flags, pe_working_set_t *data_set)
#define pe_rsc_managed
Definition: pe_types.h:273
#define crm_time_log_date
Definition: iso8601.h:67
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:563
GList * node_down
Definition: pacemaker.h:54
uint64_t flags
Definition: remote.c:215
#define XML_ATTR_TE_TARGET_RC
Definition: msg_xml.h:428
int pcmk__log_output_new(pcmk__output_t **out)
Definition: output.c:272
guint interval_ms
Definition: lrmd.h:232
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:282
xmlNode * graph
Definition: pe_types.h:199