pacemaker  2.1.7-0f7f88312f
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/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 *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, 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  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  PCMK_ACTION_NOTIFIED, 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, PCMK_ACTION_STONITH,
100  pcmk__str_none)) {
101  const char *op = g_hash_table_lookup(action->meta, "stonith_action");
102 
103  action_name = crm_strdup_printf("%s%s '%s' %s",
104  prefix, action->task, op, action_host);
105 
106  } else if (action->rsc && action_host) {
107  action_name = crm_strdup_printf("%s%s %s",
108  prefix, action->uuid, action_host);
109 
110  } else if (action_host) {
111  action_name = crm_strdup_printf("%s%s %s",
112  prefix, action->task, action_host);
113 
114  } else {
115  action_name = crm_strdup_printf("%s", action->uuid);
116  }
117 
118  if (verbose) {
119  char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
120 
121  free(action_name);
122  action_name = with_id;
123  }
124  return action_name;
125 }
126 
137 static void
138 print_cluster_status(pcmk_scheduler_t *scheduler, uint32_t show_opts,
139  uint32_t section_opts, const char *title,
140  bool print_spacer)
141 {
142  pcmk__output_t *out = scheduler->priv;
143  GList *all = NULL;
144  crm_exit_t stonith_rc = 0;
146 
147  section_opts |= pcmk_section_nodes | pcmk_section_resources;
149 
150  all = g_list_prepend(all, (gpointer) "*");
151 
152  PCMK__OUTPUT_SPACER_IF(out, print_spacer);
153  out->begin_list(out, NULL, NULL, "%s", title);
154  out->message(out, "cluster-status",
155  scheduler, state, stonith_rc, NULL,
156  false, section_opts, show_opts, NULL, 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;
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;
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->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 = "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->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->state == pe_link_dumped) {
288  optional = false;
289  style = "bold";
290  } else if ((uint32_t) before->type == pcmk__ar_none) {
291  continue;
292  } else if (pcmk_is_set(before->action->flags,
295  && (uint32_t) before->type != pcmk__ar_if_on_same_node_or_target) {
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;
333  xmlNode *cib_object = NULL;
334  clock_t start = 0;
335  clock_t end;
336  unsigned long long scheduler_flags = pcmk_sched_no_compat;
337 
338  CRM_ASSERT(out != NULL);
339 
340  cib_object = filename2xml(xml_file);
341  start = clock();
342 
343  if (pcmk_find_cib_element(cib_object, XML_CIB_TAG_STATUS) == NULL) {
344  create_xml_node(cib_object, XML_CIB_TAG_STATUS);
345  }
346 
347  if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
348  free_xml(cib_object);
349  return;
350  }
351 
352  if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
353  free_xml(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 = (repeat == 1)? cib_object : copy_xml(cib_object);
366 
367  scheduler->input = input;
368  set_effective_date(scheduler, false, use_date);
369  pcmk__schedule_actions(input, scheduler_flags, scheduler);
371  }
372 
373  end = clock();
374  out->message(out, "profile", xml_file, start, end);
375 }
376 
377 void
378 pcmk__profile_dir(const char *dir, long long repeat,
379  pcmk_scheduler_t *scheduler, const char *use_date)
380 {
381  pcmk__output_t *out = scheduler->priv;
382  struct dirent **namelist;
383 
384  int file_num = scandir(dir, &namelist, 0, alphasort);
385 
386  CRM_ASSERT(out != NULL);
387 
388  if (file_num > 0) {
389  struct stat prop;
390  char buffer[FILENAME_MAX];
391 
392  out->begin_list(out, NULL, NULL, "Timings");
393 
394  while (file_num--) {
395  if ('.' == namelist[file_num]->d_name[0]) {
396  free(namelist[file_num]);
397  continue;
398 
399  } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
400  ".xml")) {
401  free(namelist[file_num]);
402  continue;
403  }
404  snprintf(buffer, sizeof(buffer), "%s/%s",
405  dir, namelist[file_num]->d_name);
406  if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
407  profile_file(buffer, repeat, scheduler, use_date);
408  }
409  free(namelist[file_num]);
410  }
411  free(namelist);
412 
413  out->end_list(out);
414  }
415 }
416 
430 static void
431 set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
432  const char *use_date)
433 {
434  pcmk__output_t *out = scheduler->priv;
435  time_t original_date = 0;
436 
437  CRM_ASSERT(out != NULL);
438 
439  crm_element_value_epoch(scheduler->input, "execution-date", &original_date);
440 
441  if (use_date) {
442  scheduler->now = crm_time_new(use_date);
443  out->info(out, "Setting effective cluster time: %s", use_date);
444  crm_time_log(LOG_NOTICE, "Pretending 'now' is", scheduler->now,
446 
447  } else if (original_date != 0) {
448  scheduler->now = pcmk__copy_timet(original_date);
449 
450  if (print_original) {
451  char *when = crm_time_as_string(scheduler->now,
453 
454  out->info(out, "Using the original execution date of: %s", when);
455  free(when);
456  }
457  }
458 }
459 
469 static int
470 simulate_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
471 {
472  const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
473  const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY);
474 
476  out->message(out, "inject-pseudo-action", node, task);
477 
478  pcmk__update_graph(graph, action);
479  return pcmk_rc_ok;
480 }
481 
491 static int
492 simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
493 {
494  int rc;
495  lrmd_event_data_t *op = NULL;
496  int target_outcome = PCMK_OCF_OK;
497 
498  const char *rtype = NULL;
499  const char *rclass = NULL;
500  const char *resource = NULL;
501  const char *rprovider = NULL;
502  const char *resource_config_name = NULL;
503  const char *operation = crm_element_value(action->xml, "operation");
504  const char *target_rc_s = crm_meta_value(action->params,
506 
507  xmlNode *cib_node = NULL;
508  xmlNode *cib_resource = NULL;
509  xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
510 
512  char *uuid = NULL;
513  const char *router_node = crm_element_value(action->xml,
515 
516  // Certain actions don't need to be displayed or history entries
517  if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) {
518  crm_debug("No history injection for %s op on %s", operation, node);
519  goto done; // Confirm action and update graph
520  }
521 
522  if (action_rsc == NULL) { // Shouldn't be possible
523  crm_log_xml_err(action->xml, "Bad");
524  free(node);
525  return EPROTO;
526  }
527 
528  /* A resource might be known by different names in the configuration and in
529  * the action (for example, a clone instance). Grab the configuration name
530  * (which is preferred when writing history), and if necessary, the instance
531  * name.
532  */
533  resource_config_name = crm_element_value(action_rsc, XML_ATTR_ID);
534  if (resource_config_name == NULL) { // Shouldn't be possible
535  crm_log_xml_err(action->xml, "No ID");
536  free(node);
537  return EPROTO;
538  }
539  resource = resource_config_name;
540  if (pe_find_resource(fake_resource_list, resource) == NULL) {
541  const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG);
542 
543  if ((longname != NULL)
544  && (pe_find_resource(fake_resource_list, longname) != NULL)) {
545  resource = longname;
546  }
547  }
548 
549  // Certain actions need to be displayed but don't need history entries
551  PCMK_ACTION_META_DATA, NULL)) {
552  out->message(out, "inject-rsc-action", resource, operation, node,
553  (guint) 0);
554  goto done; // Confirm action and update graph
555  }
556 
557  rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS);
558  rtype = crm_element_value(action_rsc, XML_ATTR_TYPE);
559  rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER);
560 
561  pcmk__scan_min_int(target_rc_s, &target_outcome, 0);
562 
563  CRM_ASSERT(fake_cib->cmds->query(fake_cib, NULL, NULL,
565 
566  // Ensure the action node is in the CIB
568  cib_node = pcmk__inject_node(fake_cib, node,
569  ((router_node == NULL)? uuid: node));
570  free(uuid);
571  CRM_ASSERT(cib_node != NULL);
572 
573  // Add a history entry for the action
574  cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
575  resource_config_name,
576  rclass, rtype, rprovider);
577  if (cib_resource == NULL) {
578  crm_err("Could not simulate action %d history for resource %s",
579  action->id, resource);
580  free(node);
581  free_xml(cib_node);
582  return EINVAL;
583  }
584 
585  // Simulate and display an executor event for the action result
587  target_outcome, "User-injected result");
588  out->message(out, "inject-rsc-action", resource, op->op_type, node,
589  op->interval_ms);
590 
591  // Check whether action is in a list of desired simulated failures
592  for (const GList *iter = fake_op_fail_list;
593  iter != NULL; iter = iter->next) {
594  const char *spec = (const char *) iter->data;
595  char *key = NULL;
596  const char *match_name = NULL;
597 
598  // Allow user to specify anonymous clone with or without instance number
599  key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
600  op->interval_ms, node);
601  if (strncasecmp(key, spec, strlen(key)) == 0) {
602  match_name = resource;
603  }
604  free(key);
605 
606  // If not found, try the resource's name in the configuration
607  if ((match_name == NULL)
608  && (strcmp(resource, resource_config_name) != 0)) {
609 
610  key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
611  op->op_type, op->interval_ms, node);
612  if (strncasecmp(key, spec, strlen(key)) == 0) {
613  match_name = resource_config_name;
614  }
615  free(key);
616  }
617 
618  if (match_name == NULL) {
619  continue; // This failed action entry doesn't match
620  }
621 
622  // ${match_name}_${task}_${interval_in_ms}@${node}=${rc}
623  rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
624  if (rc != 1) {
625  out->err(out, "Invalid failed operation '%s' "
626  "(result code must be integer)", spec);
627  continue; // Keep checking other list entries
628  }
629 
630  out->info(out, "Pretending action %d failed with rc=%d",
631  action->id, op->rc);
633  graph->abort_priority = INFINITY;
634  pcmk__inject_failcount(out, cib_node, match_name, op->op_type,
635  op->interval_ms, op->rc);
636  break;
637  }
638 
639  pcmk__inject_action_result(cib_resource, op, target_outcome);
640  lrmd_free_event(op);
641  rc = fake_cib->cmds->modify(fake_cib, XML_CIB_TAG_STATUS, cib_node,
643  CRM_ASSERT(rc == pcmk_ok);
644 
645  done:
646  free(node);
647  free_xml(cib_node);
649  pcmk__update_graph(graph, action);
650  return pcmk_rc_ok;
651 }
652 
662 static int
663 simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
664 {
665  const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET);
666  const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK);
667  xmlNode *rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE);
668 
670  out->message(out, "inject-cluster-action", node, task, rsc);
671  pcmk__update_graph(graph, action);
672  return pcmk_rc_ok;
673 }
674 
684 static int
685 simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
686 {
687  const char *op = crm_meta_value(action->params, "stonith_action");
689 
690  out->message(out, "inject-fencing-action", target, op);
691 
692  if (!pcmk__str_eq(op, PCMK_ACTION_ON, pcmk__str_casei)) {
693  int rc = pcmk_ok;
694  GString *xpath = g_string_sized_new(512);
695 
696  // Set node state to offline
697  xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
698  false);
699 
700  CRM_ASSERT(cib_node != NULL);
701  crm_xml_add(cib_node, XML_ATTR_ORIGIN, __func__);
702  rc = fake_cib->cmds->replace(fake_cib, XML_CIB_TAG_STATUS, cib_node,
704  CRM_ASSERT(rc == pcmk_ok);
705 
706  // Simulate controller clearing node's resource history and attributes
707  pcmk__g_strcat(xpath,
708  "//" XML_CIB_TAG_STATE
709  "[@" XML_ATTR_UNAME "='", target, "']/" XML_CIB_TAG_LRM,
710  NULL);
711  fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
713 
714  g_string_truncate(xpath, 0);
715  pcmk__g_strcat(xpath,
716  "//" XML_CIB_TAG_STATE
717  "[@" XML_ATTR_UNAME "='", target, "']"
718  "/" XML_TAG_TRANSIENT_NODEATTRS, NULL);
719  fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
721 
722  free_xml(cib_node);
723  g_string_free(xpath, TRUE);
724  }
725 
727  pcmk__update_graph(graph, action);
728  free(target);
729  return pcmk_rc_ok;
730 }
731 
734  const GList *op_fail_list)
735 {
736  pcmk__graph_t *transition = NULL;
737  enum pcmk__graph_status graph_rc;
738 
739  pcmk__graph_functions_t simulation_fns = {
740  simulate_pseudo_action,
741  simulate_resource_action,
742  simulate_cluster_action,
743  simulate_fencing_action,
744  };
745 
746  out = scheduler->priv;
747 
748  fake_cib = cib;
749  fake_op_fail_list = op_fail_list;
750 
751  if (!out->is_quiet(out)) {
752  out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
753  }
754 
755  pcmk__set_graph_functions(&simulation_fns);
757  pcmk__log_graph(LOG_DEBUG, transition);
758 
759  fake_resource_list = scheduler->resources;
760  do {
761  graph_rc = pcmk__execute_graph(transition);
762  } while (graph_rc == pcmk__graph_active);
763  fake_resource_list = NULL;
764 
765  if (graph_rc != pcmk__graph_complete) {
766  out->err(out, "Transition failed: %s",
767  pcmk__graph_status2text(graph_rc));
768  pcmk__log_graph(LOG_ERR, transition);
769  out->err(out, "An invalid transition was produced");
770  }
771  pcmk__free_graph(transition);
772 
773  if (!out->is_quiet(out)) {
774  // If not quiet, we'll need the resulting CIB for later display
775  xmlNode *cib_object = NULL;
776  int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
778 
779  CRM_ASSERT(rc == pcmk_ok);
781  scheduler->input = cib_object;
782  out->end_list(out);
783  }
784  return graph_rc;
785 }
786 
787 int
789  const pcmk_injections_t *injections, unsigned int flags,
790  uint32_t section_opts, const char *use_date,
791  const char *input_file, const char *graph_file,
792  const char *dot_file)
793 {
794  int printed = pcmk_rc_no_output;
795  int rc = pcmk_rc_ok;
796  xmlNodePtr input = NULL;
797  cib_t *cib = NULL;
798 
799  rc = cib__signon_query(out, &cib, &input);
800  if (rc != pcmk_rc_ok) {
801  goto simulate_done;
802  }
803 
804  reset(scheduler, input, out, use_date, flags);
806 
807  if ((cib->variant == cib_native)
808  && pcmk_is_set(section_opts, pcmk_section_times)) {
809  if (pcmk__our_nodename == NULL) {
810  // Currently used only in the times section
811  pcmk__query_node_name(out, 0, &pcmk__our_nodename, 0);
812  }
814  }
815 
816  if (!out->is_quiet(out)) {
817  const bool show_pending = pcmk_is_set(flags, pcmk_sim_show_pending);
818 
820  printed = out->message(out, "maint-mode", scheduler->flags);
821  }
822 
824  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
825  printed = out->info(out,
826  "%d of %d resource instances DISABLED and "
827  "%d BLOCKED from further action due to failure",
831  }
832 
833  /* Most formatted output headers use caps for each word, but this one
834  * only has the first word capitalized for compatibility with pcs.
835  */
836  print_cluster_status(scheduler, (show_pending? pcmk_show_pending : 0),
837  section_opts, "Current cluster status",
838  (printed == pcmk_rc_ok));
839  printed = pcmk_rc_ok;
840  }
841 
842  // If the user requested any injections, handle them
843  if ((injections->node_down != NULL)
844  || (injections->node_fail != NULL)
845  || (injections->node_up != NULL)
846  || (injections->op_inject != NULL)
847  || (injections->ticket_activate != NULL)
848  || (injections->ticket_grant != NULL)
849  || (injections->ticket_revoke != NULL)
850  || (injections->ticket_standby != NULL)
851  || (injections->watchdog != NULL)) {
852 
853  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
854  pcmk__inject_scheduler_input(scheduler, cib, injections);
855  printed = pcmk_rc_ok;
856 
857  rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
858  if (rc != pcmk_rc_ok) {
859  rc = pcmk_legacy2rc(rc);
860  goto simulate_done;
861  }
862 
864  reset(scheduler, input, out, use_date, flags);
866  }
867 
868  if (input_file != NULL) {
869  rc = write_xml_file(input, input_file, FALSE);
870  if (rc < 0) {
871  rc = pcmk_legacy2rc(rc);
872  goto simulate_done;
873  }
874  }
875 
876  if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
877  pcmk__output_t *logger_out = NULL;
878  unsigned long long scheduler_flags = pcmk_sched_no_compat;
879 
881  scheduler_flags |= pcmk_sched_output_scores;
882  }
884  scheduler_flags |= pcmk_sched_show_utilization;
885  }
886 
887  if (pcmk_all_flags_set(scheduler->flags,
890  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
891  out->begin_list(out, NULL, NULL,
892  "Assignment Scores and Utilization Information");
893  printed = pcmk_rc_ok;
894 
896  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
897  out->begin_list(out, NULL, NULL, "Assignment Scores");
898  printed = pcmk_rc_ok;
899 
901  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
902  out->begin_list(out, NULL, NULL, "Utilization Information");
903  printed = pcmk_rc_ok;
904 
905  } else {
906  rc = pcmk__log_output_new(&logger_out);
907  if (rc != pcmk_rc_ok) {
908  goto simulate_done;
909  }
910  pe__register_messages(logger_out);
911  pcmk__register_lib_messages(logger_out);
912  scheduler->priv = logger_out;
913  }
914 
915  pcmk__schedule_actions(input, scheduler_flags, scheduler);
916 
917  if (logger_out == NULL) {
918  out->end_list(out);
919  } else {
920  logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
921  pcmk__output_free(logger_out);
922  scheduler->priv = out;
923  }
924 
925  input = NULL; /* Don't try and free it twice */
926 
927  if (graph_file != NULL) {
928  rc = write_xml_file(scheduler->graph, graph_file, FALSE);
929  if (rc < 0) {
930  rc = pcmk_rc_graph_error;
931  goto simulate_done;
932  }
933  }
934 
935  if (dot_file != NULL) {
936  rc = write_sim_dotfile(scheduler, dot_file,
939  if (rc != pcmk_rc_ok) {
940  rc = pcmk_rc_dot_error;
941  goto simulate_done;
942  }
943  }
944 
945  if (!out->is_quiet(out)) {
946  print_transition_summary(scheduler, printed == pcmk_rc_ok);
947  }
948  }
949 
950  rc = pcmk_rc_ok;
951 
953  goto simulate_done;
954  }
955 
956  PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
957  if (pcmk__simulate_transition(scheduler, cib, injections->op_fail)
960  }
961 
962  if (out->is_quiet(out)) {
963  goto simulate_done;
964  }
965 
966  set_effective_date(scheduler, true, use_date);
967 
970  }
973  }
974 
976  print_cluster_status(scheduler, 0, section_opts, "Revised Cluster Status",
977  true);
978 
979 simulate_done:
981  return rc;
982 }
983 
984 int
986  const pcmk_injections_t *injections, unsigned int flags,
987  unsigned int section_opts, const char *use_date,
988  const char *input_file, const char *graph_file,
989  const char *dot_file)
990 {
991  pcmk__output_t *out = NULL;
992  int rc = pcmk_rc_ok;
993 
994  rc = pcmk__xml_output_new(&out, xml);
995  if (rc != pcmk_rc_ok) {
996  return rc;
997  }
998 
1001 
1002  rc = pcmk__simulate(scheduler, out, injections, flags, section_opts,
1003  use_date, input_file, graph_file, dot_file);
1004  pcmk__xml_output_finish(out, xml);
1005  return rc;
1006 }
#define XML_ATTR_ID_LONG
Definition: msg_xml.h:159
void(* end_list)(pcmk__output_t *out)
enum pe_link_state state
Definition: actions.h:383
GList * op_inject
Definition: pacemaker.h:61
Actions are ordered if on same node (or migration target for migrate_to)
#define INFINITY
Definition: crm.h:98
Whether action should not be executed.
Definition: actions.h:244
enum pcmk__graph_status pcmk__simulate_transition(pcmk_scheduler_t *scheduler, cib_t *cib, const GList *op_fail_list)
#define pe__set_action_flags(action, flags_to_set)
Definition: internal.h:76
Control output from tools.
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:1022
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:160
pcmk_action_t * action
Definition: actions.h:385
crm_time_t * pcmk__copy_timet(time_t source)
Definition: iso8601.c:1391
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
#define CRM_OP_REPROBE
Definition: crm.h:151
GList * ticket_activate
Definition: pacemaker.h:74
#define PCMK_ACTION_ON
Definition: actions.h:63
xmlNode * first_named_child(const xmlNode *parent, const char *name)
Definition: xml.c:2484
G_GNUC_INTERNAL xmlNode * pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up)
void pe_reset_working_set(pcmk_scheduler_t *scheduler)
Reset scheduler data to default state without freeing it.
Definition: status.c:338
bool(* is_quiet)(pcmk__output_t *out)
Implementation of pcmk_action_t.
Definition: actions.h:390
#define PCMK_ACTION_META_DATA
Definition: actions.h:56
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:420
Whether node scores should be output instead of logged.
Definition: scheduler.h:158
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
#define pe__set_working_set_flags(scheduler, flags_to_set)
Definition: internal.h:52
int write_xml_file(const xmlNode *xml, const char *filename, gboolean compress)
Write XML to a file.
Definition: xml.c:1284
G_GNUC_INTERNAL xmlNode * pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid)
High Level API.
void pcmk__schedule_actions(xmlNode *cib, unsigned long long flags, pcmk_scheduler_t *scheduler)
char * crm_system_name
Definition: utils.c:51
enum crm_exit_e crm_exit_t
GList * actions
Scheduled actions.
Definition: scheduler.h:204
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:222
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:276
enum ocf_exitcode rc
Definition: lrmd_events.h:63
xmlNode * filename2xml(const char *filename)
Definition: xml.c:990
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
gboolean cluster_status(pcmk_scheduler_t *scheduler)
Definition: status.c:71
const char * action
Definition: pcmk_fence.c:30
int ninstances
Total number of resource instances.
Definition: scheduler.h:224
gboolean validate_xml(xmlNode *xml_blob, const char *validation, gboolean to_logs)
Definition: schemas.c:673
GList * resources
Resources in cluster.
Definition: scheduler.h:196
#define XML_ATTR_ORIGIN
Definition: msg_xml.h:151
Scheduler API.
#define PCMK__OUTPUT_SPACER_IF(out_obj, cond)
#define XML_LRM_ATTR_TASK_KEY
Definition: msg_xml.h:307
xmlNode * copy_xml(xmlNode *src_node)
Definition: xml.c:789
#define XML_LRM_ATTR_TASK
Definition: msg_xml.h:306
Whether to show node and resource utilization (in log or output)
Definition: scheduler.h:161
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
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id_rh)
Definition: status.c:391
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:345
#define crm_debug(fmt, args...)
Definition: logging.h:386
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)
pcmk__graph_status
void pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, const pcmk_injections_t *injections)
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
Definition: nvpair.c:644
#define XML_ATTR_ID
Definition: msg_xml.h:156
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
Definition: nvpair.c:447
#define XML_CIB_TAG_RESOURCE
Definition: msg_xml.h:235
void pcmk__set_graph_functions(pcmk__graph_functions_t *fns)
#define XML_CIB_TAG_STATE
Definition: msg_xml.h:222
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition: cib.c:172
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
void * priv
For Pacemaker use only.
Definition: scheduler.h:229
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:99
int blocked_resources
Number of blocked resources in cluster.
Definition: scheduler.h:219
int(* modify)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:180
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:281
void pcmk__register_lib_messages(pcmk__output_t *out)
Definition: pcmk_output.c:2403
void pcmk__free_graph(pcmk__graph_t *graph)
#define XML_ATTR_UNAME
Definition: msg_xml.h:178
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:638
Action completed, result is known.
Definition: results.h:318
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition: cib_types.h:156
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:568
#define PCMK_ACTION_STONITH
Definition: actions.h:73
#define PCMK_ACTION_NOTIFIED
Definition: actions.h:60
#define crm_time_log(level, prefix, dt, flags)
Definition: iso8601.h:60
Success.
Definition: results.h:240
int pcmk_legacy2rc(int legacy_rc)
Definition: results.c:559
G_GNUC_INTERNAL void pcmk__output_actions(pcmk_scheduler_t *scheduler)
int(* replace)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition: cib_types.h:187
void pe__register_messages(pcmk__output_t *out)
Definition: pe_output.c:3162
#define XML_LRM_ATTR_ROUTER_NODE
Definition: msg_xml.h:314
#define PCMK_ACTION_DELETE
Definition: actions.h:48
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:42
void free_xml(xmlNode *child)
Definition: xml.c:783
xmlNode * input
CIB XML.
Definition: scheduler.h:175
int cib__clean_up_connection(cib_t **cib)
Definition: cib_utils.c:1076
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:43
#define PCMK_ACTION_CANCEL
Definition: actions.h:45
Whether sensitive resource attributes have been masked.
Definition: scheduler.h:146
void cleanup_calculations(pcmk_scheduler_t *scheduler)
Reset scheduler data to defaults without freeing it or constraints.
Definition: status.c:279
#define XML_LRM_ATTR_TARGET_UUID
Definition: msg_xml.h:309
void pcmk__output_free(pcmk__output_t *out)
Definition: output.c:28
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.
const char * target
Definition: pcmk_fence.c:29
pcmk_pacemakerd_state
const char * localhost
Definition: scheduler.h:216
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition: actions.c:183
#define crm_log_xml_err(xml, text)
Definition: logging.h:390
Whether action is runnable.
Definition: actions.h:241
GList * ticket_revoke
Definition: pacemaker.h:70
#define crm_err(fmt, args...)
Definition: logging.h:381
pcmk_scheduler_t * scheduler
Whether action does not require invoking an agent.
Definition: actions.h:238
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:170
int disabled_resources
Number of disabled resources in cluster.
Definition: scheduler.h:220
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
void lrmd_free_event(lrmd_event_data_t *event)
Free an executor event.
Definition: lrmd_client.c:243
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:304
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:332
const char * pcmk__graph_status2text(enum pcmk__graph_status state)
enum pe_action_flags flags
Group of enum pe_action_flags.
Definition: actions.h:409
#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__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)
#define XML_CIB_TAG_STATUS
Definition: msg_xml.h:204
Whether action has been added to transition graph.
Definition: actions.h:256
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:189
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)
#define XML_LRM_ATTR_TARGET
Definition: msg_xml.h:308
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition: scheduler.h:183
gboolean cli_config_update(xmlNode **xml, int *best_version, gboolean to_logs)
Definition: schemas.c:1134
Whether resource is managed.
Definition: resources.h:106
enum pe_ordering type
Definition: actions.h:380
Whether cluster is in maintenance mode (via maintenance-mode property)
Definition: scheduler.h:77
crm_time_t * now
Current time for evaluation purposes.
Definition: scheduler.h:176
#define crm_time_log_date
Definition: iso8601.h:67
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition: strings.c:560
GList * node_down
Definition: pacemaker.h:54
uint64_t flags
Definition: remote.c:215
#define XML_ATTR_TE_TARGET_RC
Definition: msg_xml.h:419
int pcmk__log_output_new(pcmk__output_t **out)
Definition: output.c:272
#define PCMK_ACTION_NOTIFY
Definition: actions.h:61
No relation (compare with equality rather than bit set)
#define XML_AGENT_ATTR_CLASS
Definition: msg_xml.h:280
xmlNode * graph
Transition graph.
Definition: scheduler.h:212