pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
pcmk_simulate.c
Go to the documentation of this file.
1/*
2 * Copyright 2021-2025 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>
15#include <pacemaker-internal.h>
16#include <pacemaker.h>
17
18#include <stdint.h> // uint32_t, uint64_t
19#include <sys/types.h>
20#include <sys/stat.h>
21#include <unistd.h>
22
24
25static const char *profiling_dir = NULL;
26static pcmk__output_t *out = NULL;
27static cib_t *fake_cib = NULL;
28static GList *fake_resource_list = NULL;
29static const GList *fake_op_fail_list = NULL;
30
31static void set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
32 const char *use_date);
33
44static char *
45create_action_name(const pcmk_action_t *action, bool verbose)
46{
47 char *action_name = NULL;
48 const char *prefix = "";
49 const char *action_host = NULL;
50 const char *history_id = NULL;
51 const char *task = action->task;
52
53 if (action->node != NULL) {
54 action_host = action->node->priv->name;
55 } else if (!pcmk_is_set(action->flags, pcmk__action_pseudo)) {
56 action_host = "<none>";
57 }
58
59 if (pcmk__str_eq(action->task, PCMK_ACTION_CANCEL, pcmk__str_none)) {
60 prefix = "Cancel ";
61 task = action->cancel_task;
62 }
63
64 if (action->rsc != NULL) {
65 history_id = action->rsc->priv->history_id;
66 }
67
68 if (history_id != NULL) {
69 char *key = NULL;
70 guint interval_ms = 0;
71
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 pcmk__assert((n_type != NULL) && (n_task != NULL));
85 key = pcmk__notify_key(history_id, n_type, n_task);
86 } else {
87 key = pcmk__op_key(history_id, task, interval_ms);
88 }
89
90 if (action_host != NULL) {
91 action_name = crm_strdup_printf("%s%s %s",
92 prefix, key, action_host);
93 } else {
94 action_name = crm_strdup_printf("%s%s", prefix, key);
95 }
96 free(key);
97
98 } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
100 const char *op = g_hash_table_lookup(action->meta,
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
137static void
138print_cluster_status(pcmk_scheduler_t *scheduler, uint32_t show_opts,
139 uint32_t section_opts, const char *title,
140 bool print_spacer)
141{
143 GList *all = NULL;
144 crm_exit_t stonith_rc = 0;
146
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 pcmk__fence_history_none, section_opts, show_opts, NULL,
157 all, all);
158 out->end_list(out);
159
160 g_list_free(all);
161}
162
170static void
171print_transition_summary(pcmk_scheduler_t *scheduler, bool print_spacer)
172{
174
175 PCMK__OUTPUT_SPACER_IF(out, print_spacer);
176 out->begin_list(out, NULL, NULL, "Transition Summary");
178 out->end_list(out);
179}
180
191static void
192reset(pcmk_scheduler_t *scheduler, xmlNodePtr input, pcmk__output_t *out,
193 const char *use_date, unsigned int flags)
194{
196
198 scheduler->priv->out = out;
199 set_effective_date(scheduler, true, use_date);
202 }
205 }
208 }
210}
211
225static int
226write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file,
227 bool all_actions, bool verbose)
228{
229 GList *iter = NULL;
230 FILE *dot_strm = fopen(dot_file, "w");
231
232 if (dot_strm == NULL) {
233 return errno;
234 }
235
236 fprintf(dot_strm, " digraph \"g\" {\n");
237 for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
238 pcmk_action_t *action = (pcmk_action_t *) iter->data;
239 const char *style = "dashed";
240 const char *font = "black";
241 const char *color = NULL;
242 char *action_name = create_action_name(action, verbose);
243
245 font = "orange";
246 }
247
249 style = PCMK__VALUE_BOLD;
250 color = "green";
251
252 } else if ((action->rsc != NULL)
253 && !pcmk_is_set(action->rsc->flags, pcmk__rsc_managed)) {
254 color = "red";
255 font = "purple";
256 if (!all_actions) {
257 goto do_not_write;
258 }
259
260 } else if (pcmk_is_set(action->flags, pcmk__action_optional)) {
261 color = "blue";
262 if (!all_actions) {
263 goto do_not_write;
264 }
265
266 } else {
267 color = "red";
269 }
270
272 fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
273 action_name, style, color, font);
274 do_not_write:
275 free(action_name);
276 }
277
278 for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
279 pcmk_action_t *action = (pcmk_action_t *) iter->data;
280
281 for (GList *before_iter = action->actions_before;
282 before_iter != NULL; before_iter = before_iter->next) {
283
284 pcmk__related_action_t *before = before_iter->data;
285
286 char *before_name = NULL;
287 char *after_name = NULL;
288 const char *style = "dashed";
289 bool optional = true;
290
291 if (before->graphed) {
292 optional = false;
293 style = PCMK__VALUE_BOLD;
294 } else if (before->flags == pcmk__ar_none) {
295 continue;
296 } else if (pcmk_is_set(before->action->flags,
300 optional = false;
301 }
302
303 if (all_actions || !optional) {
304 before_name = create_action_name(before->action, verbose);
305 after_name = create_action_name(action, verbose);
306 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
307 before_name, after_name, style);
308 free(before_name);
309 free(after_name);
310 }
311 }
312 }
313
314 fprintf(dot_strm, "}\n");
315 fflush(dot_strm);
316 fclose(dot_strm);
317 return pcmk_rc_ok;
318}
319
330static int
331profile_filter(const struct dirent *entry)
332{
333 const char *filename = entry->d_name;
334 char *buf = NULL;
335 struct stat sb;
336 int rc = 0;
337
338 if (pcmk__str_any_of(filename, ".", "..", NULL)) {
339 // Skip current (".") and parent ("..") directory links
340 goto done;
341 }
342 if (filename[0] == '.') {
343 crm_trace("Not profiling hidden file '%s'", filename);
344 goto done;
345 }
346 if (!pcmk__ends_with_ext(filename, ".xml")) {
347 crm_trace("Not profiling file '%s' without '.xml' extension", filename);
348 goto done;
349 }
350
351 buf = crm_strdup_printf("%s/%s", profiling_dir, filename);
352 if ((stat(buf, &sb) != 0) || !S_ISREG(sb.st_mode)) {
353 crm_trace("Not profiling file '%s': not a regular file", filename);
354 goto done;
355 }
356
357 rc = 1;
358
359done:
360 free(buf);
361 return rc;
362}
363
379static void
380profile_file(const char *xml_file, unsigned int repeat,
381 pcmk_scheduler_t *scheduler, uint64_t flags, const char *use_date)
382{
384 xmlNode *cib_object = NULL;
385 clock_t start = 0;
386 clock_t end;
387
388 pcmk__assert(out != NULL);
389
390 cib_object = pcmk__xml_read(xml_file);
391 start = clock();
392
393 if (pcmk_find_cib_element(cib_object, PCMK_XE_STATUS) == NULL) {
394 pcmk__xe_create(cib_object, PCMK_XE_STATUS);
395 }
396
397 if (pcmk__update_configured_schema(&cib_object, false) != pcmk_rc_ok) {
398 goto done;
399 }
400
401 if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) {
402 goto done;
403 }
404
405 for (int i = 0; i < repeat; ++i) {
407
408 scheduler->input = pcmk__xml_copy(NULL, cib_object);
410 set_effective_date(scheduler, false, use_date);
412 }
413
415 end = clock();
416 out->message(out, "profile", xml_file, start, end);
417
418done:
419 pcmk__xml_free(cib_object);
420}
421
422int
423pcmk__profile_dir(pcmk__output_t *out, uint32_t flags, const char *dir,
424 unsigned int repeat, const char *use_date)
425{
427 uint64_t scheduler_flags = pcmk__sched_none;
428 struct dirent **namelist = NULL;
429 int num_files = 0;
430 int rc = pcmk_rc_ok;
431
432 pcmk__assert(out != NULL);
433
435 if (scheduler == NULL) {
436 return ENOMEM;
437 }
438
439 scheduler->priv->out = out;
441 scheduler_flags |= pcmk__sched_output_scores;
442 }
444 scheduler_flags |= pcmk__sched_show_utilization;
445 }
446
447 // Hack to pass user data to profile_filter
448 profiling_dir = dir;
449 num_files = scandir(dir, &namelist, profile_filter, alphasort);
450 profiling_dir = NULL;
451
452 if (num_files < 0) {
453 rc = errno;
454 goto done;
455 }
456 if (num_files == 0) {
457 goto done;
458 }
459
460 out->begin_list(out, NULL, NULL, "Timings");
461
462 for (int i = 0; i < num_files; i++) {
463 // glibc doesn't enforce PATH_MAX, so don't limit the buffer size
464 char *path = crm_strdup_printf("%s/%s", dir, namelist[i]->d_name);
465
466 profile_file(path, repeat, scheduler, scheduler_flags, use_date);
467 free(path);
468 free(namelist[i]);
469 }
470 out->end_list(out);
471
472done:
474 free(namelist);
475 return rc;
476}
477
491static void
492set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
493 const char *use_date)
494{
496 time_t original_date = 0;
497
498 pcmk__assert(out != NULL);
499
501 &original_date);
502
503 if (use_date) {
504 scheduler->priv->now = crm_time_new(use_date);
505 out->info(out, "Setting effective cluster time: %s", use_date);
506 crm_time_log(LOG_NOTICE, "Pretending 'now' is", scheduler->priv->now,
508
509 } else if (original_date != 0) {
510 scheduler->priv->now = pcmk__copy_timet(original_date);
511
512 if (print_original) {
513 char *when = crm_time_as_string(scheduler->priv->now,
516
517 out->info(out, "Using the original execution date of: %s", when);
518 free(when);
519 }
520 }
521}
522
532static int
533simulate_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
534{
535 const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE);
536 const char *task = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
537
539 out->message(out, "inject-pseudo-action", node, task);
540
542 return pcmk_rc_ok;
543}
544
554static int
555simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
556{
557 int rc;
558 lrmd_event_data_t *op = NULL;
559 int target_outcome = PCMK_OCF_OK;
560
561 const char *rtype = NULL;
562 const char *rclass = NULL;
563 const char *resource = NULL;
564 const char *rprovider = NULL;
565 const char *resource_config_name = NULL;
566 const char *operation = crm_element_value(action->xml, PCMK_XA_OPERATION);
567 const char *target_rc_s = crm_meta_value(action->params,
569
570 xmlNode *cib_node = NULL;
571 xmlNode *cib_resource = NULL;
572 xmlNode *action_rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE,
573 NULL, NULL);
574
576 char *uuid = NULL;
577 const char *router_node = crm_element_value(action->xml,
579
580 // Certain actions don't need to be displayed or history entries
581 if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) {
582 crm_debug("No history injection for %s op on %s", operation, node);
583 goto done; // Confirm action and update graph
584 }
585
586 if (action_rsc == NULL) { // Shouldn't be possible
587 crm_log_xml_err(action->xml, "Bad");
588 free(node);
589 return EPROTO;
590 }
591
592 /* A resource might be known by different names in the configuration and in
593 * the action (for example, a clone instance). Grab the configuration name
594 * (which is preferred when writing history), and if necessary, the instance
595 * name.
596 */
597 resource_config_name = crm_element_value(action_rsc, PCMK_XA_ID);
598 if (resource_config_name == NULL) { // Shouldn't be possible
599 crm_log_xml_err(action->xml, "No ID");
600 free(node);
601 return EPROTO;
602 }
603 resource = resource_config_name;
604 if (pe_find_resource(fake_resource_list, resource) == NULL) {
605 const char *longname = crm_element_value(action_rsc, PCMK__XA_LONG_ID);
606
607 if ((longname != NULL)
608 && (pe_find_resource(fake_resource_list, longname) != NULL)) {
609 resource = longname;
610 }
611 }
612
613 // Certain actions need to be displayed but don't need history entries
615 PCMK_ACTION_META_DATA, NULL)) {
616 out->message(out, "inject-rsc-action", resource, operation, node,
617 (guint) 0);
618 goto done; // Confirm action and update graph
619 }
620
621 rclass = crm_element_value(action_rsc, PCMK_XA_CLASS);
622 rtype = crm_element_value(action_rsc, PCMK_XA_TYPE);
623 rprovider = crm_element_value(action_rsc, PCMK_XA_PROVIDER);
624
625 pcmk__scan_min_int(target_rc_s, &target_outcome, 0);
626
627 pcmk__assert(fake_cib->cmds->query(fake_cib, NULL, NULL,
629
630 // Ensure the action node is in the CIB
632 cib_node = pcmk__inject_node(fake_cib, node,
633 ((router_node == NULL)? uuid: node));
634 free(uuid);
635 pcmk__assert(cib_node != NULL);
636
637 // Add a history entry for the action
638 cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
639 resource_config_name,
640 rclass, rtype, rprovider);
641 if (cib_resource == NULL) {
642 crm_err("Could not simulate action %d history for resource %s",
643 action->id, resource);
644 free(node);
645 pcmk__xml_free(cib_node);
646 return EINVAL;
647 }
648
649 // Simulate and display an executor event for the action result
651 target_outcome, "User-injected result");
652 out->message(out, "inject-rsc-action", resource, op->op_type, node,
653 op->interval_ms);
654
655 // Check whether action is in a list of desired simulated failures
656 for (const GList *iter = fake_op_fail_list;
657 iter != NULL; iter = iter->next) {
658 const char *spec = (const char *) iter->data;
659 char *key = NULL;
660 const char *match_name = NULL;
661 const char *offset = NULL;
662
663 // Allow user to specify anonymous clone with or without instance number
664 key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
665 op->interval_ms, node);
666 if (strncasecmp(key, spec, strlen(key)) == 0) {
667 match_name = resource;
668 }
669 free(key);
670
671 // If not found, try the resource's name in the configuration
672 if ((match_name == NULL)
673 && (strcmp(resource, resource_config_name) != 0)) {
674
675 key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
676 op->op_type, op->interval_ms, node);
677 if (strncasecmp(key, spec, strlen(key)) == 0) {
678 match_name = resource_config_name;
679 }
680 free(key);
681 }
682
683 if (match_name == NULL) {
684 continue; // This failed action entry doesn't match
685 }
686
687 // ${match_name}_${task}_${interval_in_ms}@${node}=${rc}
688 rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
689 if (rc != 1) {
690 out->err(out, "Invalid failed operation '%s' "
691 "(result code must be integer)", spec);
692 continue; // Keep checking other list entries
693 }
694
695 out->info(out, "Pretending action %d failed with rc=%d",
696 action->id, op->rc);
699
700 if (pcmk__str_eq(op->op_type, PCMK_ACTION_START, pcmk__str_none)) {
701 offset = pcmk__s(graph->failed_start_offset, PCMK_VALUE_INFINITY);
702
703 } else if (pcmk__str_eq(op->op_type, PCMK_ACTION_STOP,
705 offset = pcmk__s(graph->failed_stop_offset, PCMK_VALUE_INFINITY);
706 }
707
708 pcmk__inject_failcount(out, fake_cib, cib_node, match_name, op->op_type,
709 op->interval_ms, op->rc,
710 pcmk_str_is_infinity(offset));
711 break;
712 }
713
714 pcmk__inject_action_result(cib_resource, op, node, target_outcome);
715 lrmd_free_event(op);
716 rc = fake_cib->cmds->modify(fake_cib, PCMK_XE_STATUS, cib_node,
718 pcmk__assert(rc == pcmk_ok);
719
720 done:
721 free(node);
722 pcmk__xml_free(cib_node);
725 return pcmk_rc_ok;
726}
727
737static int
738simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
739{
740 const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE);
741 const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
742 xmlNode *rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
743 NULL);
744
746 out->message(out, "inject-cluster-action", node, task, rsc);
748 return pcmk_rc_ok;
749}
750
760static int
761simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
762{
763 const char *op = crm_meta_value(action->params, PCMK__META_STONITH_ACTION);
765
766 out->message(out, "inject-fencing-action", target, op);
767
768 if (!pcmk__str_eq(op, PCMK_ACTION_ON, pcmk__str_casei)) {
769 int rc = pcmk_ok;
770 GString *xpath = g_string_sized_new(512);
771
772 // Set node state to offline
773 xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
774 false);
775
776 pcmk__assert(cib_node != NULL);
777 crm_xml_add(cib_node, PCMK_XA_CRM_DEBUG_ORIGIN, __func__);
778 rc = fake_cib->cmds->replace(fake_cib, PCMK_XE_STATUS, cib_node,
780 pcmk__assert(rc == pcmk_ok);
781
782 // Simulate controller clearing node's resource history and attributes
783 pcmk__g_strcat(xpath,
785 "[@" PCMK_XA_UNAME "='", target, "']/" PCMK__XE_LRM,
786 NULL);
787 fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
789
790 g_string_truncate(xpath, 0);
791 pcmk__g_strcat(xpath,
793 "[@" PCMK_XA_UNAME "='", target, "']"
795 fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
797
798 pcmk__xml_free(cib_node);
799 g_string_free(xpath, TRUE);
800 }
801
804 free(target);
805 return pcmk_rc_ok;
806}
807
810 const GList *op_fail_list)
811{
812 pcmk__graph_t *transition = NULL;
813 enum pcmk__graph_status graph_rc;
814
815 pcmk__graph_functions_t simulation_fns = {
816 simulate_pseudo_action,
817 simulate_resource_action,
818 simulate_cluster_action,
819 simulate_fencing_action,
820 };
821
822 out = scheduler->priv->out;
823
824 fake_cib = cib;
825 fake_op_fail_list = op_fail_list;
826
827 if (!out->is_quiet(out)) {
828 out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
829 }
830
831 pcmk__set_graph_functions(&simulation_fns);
833 pcmk__log_graph(LOG_DEBUG, transition);
834
835 fake_resource_list = scheduler->priv->resources;
836 do {
837 graph_rc = pcmk__execute_graph(transition);
838 } while (graph_rc == pcmk__graph_active);
839 fake_resource_list = NULL;
840
841 if (graph_rc != pcmk__graph_complete) {
842 out->err(out, "Transition failed: %s",
843 pcmk__graph_status2text(graph_rc));
844 pcmk__log_graph(LOG_ERR, transition);
845 out->err(out, "An invalid transition was produced");
846 }
847 pcmk__free_graph(transition);
848
849 if (!out->is_quiet(out)) {
850 // If not quiet, we'll need the resulting CIB for later display
851 xmlNode *cib_object = NULL;
852 int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
854
855 pcmk__assert(rc == pcmk_ok);
857 scheduler->input = cib_object;
858 out->end_list(out);
859 }
860 return graph_rc;
861}
862
863int
865 const pcmk_injections_t *injections, uint32_t flags,
866 uint32_t section_opts, const char *use_date,
867 const char *input_file, const char *graph_file,
868 const char *dot_file)
869{
870 int printed = pcmk_rc_no_output;
871 int rc = pcmk_rc_ok;
872 xmlNodePtr input = NULL;
873 cib_t *cib = NULL;
874
875 rc = cib__signon_query(out, &cib, &input);
876 if (rc != pcmk_rc_ok) {
877 goto simulate_done;
878 }
879
880 reset(scheduler, input, out, use_date, flags);
881
882 if (!out->is_quiet(out)) {
883 const bool show_pending = pcmk_is_set(flags, pcmk_sim_show_pending);
884
886 printed = out->message(out, "maint-mode", scheduler->flags);
887 }
888
890 || (scheduler->priv->blocked_resources > 0)) {
891
892 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
893 printed = out->info(out,
894 "%d of %d resource instances DISABLED and "
895 "%d BLOCKED from further action due to failure",
899 }
900
901 /* Most formatted output headers use caps for each word, but this one
902 * only has the first word capitalized for compatibility with pcs.
903 */
904 print_cluster_status(scheduler, (show_pending? pcmk_show_pending : 0),
905 section_opts, "Current cluster status",
906 (printed == pcmk_rc_ok));
907 printed = pcmk_rc_ok;
908 }
909
910 // If the user requested any injections, handle them
911 if ((injections->node_down != NULL)
912 || (injections->node_fail != NULL)
913 || (injections->node_up != NULL)
914 || (injections->op_inject != NULL)
915 || (injections->ticket_activate != NULL)
916 || (injections->ticket_grant != NULL)
917 || (injections->ticket_revoke != NULL)
918 || (injections->ticket_standby != NULL)
919 || (injections->watchdog != NULL)) {
920
921 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
922 pcmk__inject_scheduler_input(scheduler, cib, injections);
923 printed = pcmk_rc_ok;
924
925 rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
926 if (rc != pcmk_rc_ok) {
927 rc = pcmk_legacy2rc(rc);
928 goto simulate_done;
929 }
930
931 reset(scheduler, input, out, use_date, flags);
932 }
933
934 if (input_file != NULL) {
935 rc = pcmk__xml_write_file(input, input_file, false);
936 if (rc != pcmk_rc_ok) {
937 goto simulate_done;
938 }
939 }
940
941 if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
942 pcmk__output_t *logger_out = NULL;
943
944 if (pcmk_all_flags_set(scheduler->flags,
947 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
948 out->begin_list(out, NULL, NULL,
949 "Assignment Scores and Utilization Information");
950 printed = pcmk_rc_ok;
951
953 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
954 out->begin_list(out, NULL, NULL, "Assignment Scores");
955 printed = pcmk_rc_ok;
956
958 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
959 out->begin_list(out, NULL, NULL, "Utilization Information");
960 printed = pcmk_rc_ok;
961
962 } else {
963 rc = pcmk__log_output_new(&logger_out);
964 if (rc != pcmk_rc_ok) {
965 goto simulate_done;
966 }
967 pe__register_messages(logger_out);
968 pcmk__register_lib_messages(logger_out);
969 scheduler->priv->out = logger_out;
970 }
971
973
974 if (logger_out == NULL) {
975 out->end_list(out);
976 } else {
977 logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
978 pcmk__output_free(logger_out);
979 scheduler->priv->out = out;
980 }
981
982 input = NULL; /* Don't try and free it twice */
983
984 if (graph_file != NULL) {
985 rc = pcmk__xml_write_file(scheduler->priv->graph, graph_file,
986 false);
987 if (rc != pcmk_rc_ok) {
989 goto simulate_done;
990 }
991 }
992
993 if (dot_file != NULL) {
994 rc = write_sim_dotfile(scheduler, dot_file,
997 if (rc != pcmk_rc_ok) {
999 goto simulate_done;
1000 }
1001 }
1002
1003 if (!out->is_quiet(out)) {
1004 print_transition_summary(scheduler, printed == pcmk_rc_ok);
1005 }
1006 }
1007
1008 rc = pcmk_rc_ok;
1009
1011 goto simulate_done;
1012 }
1013
1014 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
1015 if (pcmk__simulate_transition(scheduler, cib, injections->op_fail)
1018 }
1019
1020 if (out->is_quiet(out)) {
1021 goto simulate_done;
1022 }
1023
1024 set_effective_date(scheduler, true, use_date);
1025
1028 }
1031 }
1032
1034 print_cluster_status(scheduler, 0, section_opts, "Revised Cluster Status",
1035 true);
1036
1037simulate_done:
1039 return rc;
1040}
1041
1042// @COMPAT Use uint32_t for flags
1043int
1045 const pcmk_injections_t *injections, unsigned int flags,
1046 unsigned int section_opts, const char *use_date,
1047 const char *input_file, const char *graph_file,
1048 const char *dot_file)
1049{
1050 pcmk__output_t *out = NULL;
1051 int rc = pcmk_rc_ok;
1052
1053 rc = pcmk__xml_output_new(&out, xml);
1054 if (rc != pcmk_rc_ok) {
1055 return rc;
1056 }
1057
1060
1061 rc = pcmk__simulate(scheduler, out, injections, (uint32_t) flags,
1062 (uint32_t) section_opts, use_date, input_file,
1063 graph_file, dot_file);
1065 return rc;
1066}
@ pcmk__ar_none
No relation (compare with equality rather than bit set)
@ pcmk__ar_if_on_same_node_or_target
Actions are ordered if on same node (or migration target for migrate_to)
#define PCMK_ACTION_STOP
Definition actions.h:66
#define PCMK_ACTION_CANCEL
Definition actions.h:36
#define PCMK_ACTION_META_DATA
Definition actions.h:47
#define PCMK_ACTION_START
Definition actions.h:63
#define PCMK_ACTION_NOTIFIED
Definition actions.h:52
#define PCMK_ACTION_DELETE
Definition actions.h:39
#define PCMK_ACTION_STONITH
Definition actions.h:65
#define PCMK_ACTION_ON
Definition actions.h:55
#define PCMK_ACTION_NOTIFY
Definition actions.h:53
@ pcmk__action_runnable
@ pcmk__action_added_to_graph
@ pcmk__action_optional
@ pcmk__action_pseudo
char * pcmk__notify_key(const char *rsc_id, const char *notify_type, const char *op_type)
Definition actions.c:365
#define pcmk__set_action_flags(action, flags_to_set)
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:225
#define PCMK__OP_FMT
printf-style format to create operation key from resource, action, interval
int cib__clean_up_connection(cib_t **cib)
Definition cib_utils.c:942
int cib__signon_query(pcmk__output_t *out, cib_t **cib, xmlNode **cib_object)
Definition cib_utils.c:863
const char * path
Definition cib.c:28
@ cib_xpath
Definition cib_types.h:58
@ cib_sync_call
Definition cib_types.h:112
xmlNode * pcmk_find_cib_element(xmlNode *cib, const char *element_name)
Find an element in the CIB.
Definition cib.c:172
uint64_t flags
Definition remote.c:3
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
#define CRM_OP_REPROBE
Definition crm.h:128
char * crm_system_name
Definition utils.c:45
pcmk_pacemakerd_state
@ pcmk_pacemakerd_state_invalid
#define crm_time_log_timeofday
Definition iso8601.h:68
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
#define crm_time_log_date
Definition iso8601.h:67
crm_time_t * crm_time_new(const char *string)
Definition iso8601.c:112
#define crm_time_log(level, prefix, dt, flags)
Definition iso8601.h:60
crm_time_t * pcmk__copy_timet(time_t source)
Definition iso8601.c:1488
G_GNUC_INTERNAL xmlNode * pcmk__inject_action_result(xmlNode *cib_resource, lrmd_event_data_t *op, const char *node, int target_rc)
G_GNUC_INTERNAL xmlNode * pcmk__inject_node(cib_t *cib_conn, const char *node, const char *uuid)
G_GNUC_INTERNAL xmlNode * pcmk__inject_node_state_change(cib_t *cib_conn, const char *node, bool up)
G_GNUC_INTERNAL void pcmk__inject_scheduler_input(pcmk_scheduler_t *scheduler, cib_t *cib, const pcmk_injections_t *injections)
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)
G_GNUC_INTERNAL void pcmk__output_actions(pcmk_scheduler_t *scheduler)
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 CRM_LOG_ASSERT(expr)
Definition logging.h:196
#define crm_log_xml_err(xml, text)
Definition logging.h:373
#define crm_debug(fmt, args...)
Definition logging.h:368
#define crm_err(fmt, args...)
Definition logging.h:357
#define crm_trace(fmt, args...)
Definition logging.h:370
void lrmd_free_event(lrmd_event_data_t *event)
Free an executor event.
pcmk_scheduler_t * scheduler
xmlNode * input
const char * crm_meta_value(GHashTable *hash, const char *field)
Get the value of a meta-attribute.
Definition nvpair.c:553
#define PCMK_META_INTERVAL
Definition options.h:92
#define PCMK_VALUE_INFINITY
Definition options.h:165
#define PCMK__META_ON_NODE_UUID
#define PCMK__META_ON_NODE
#define PCMK__META_OP_TARGET_RC
#define PCMK__VALUE_BOLD
#define PCMK__META_STONITH_ACTION
Control output from tools.
@ pcmk_show_failed_detail
Definition output.h:67
@ pcmk_show_pending
Definition output.h:65
@ pcmk_show_inactive_rscs
Definition output.h:63
@ pcmk_section_nodes
Definition output.h:32
@ pcmk_section_resources
Definition output.h:33
void pcmk__xml_output_finish(pcmk__output_t *out, crm_exit_t exit_status, xmlNodePtr *xml)
Definition output.c:273
void pcmk__output_free(pcmk__output_t *out)
Definition output.c:30
int pcmk__xml_output_new(pcmk__output_t **out, xmlNodePtr *xml)
Definition output.c:246
#define PCMK__OUTPUT_SPACER_IF(out_obj, cond)
int pcmk__log_output_new(pcmk__output_t **out)
Definition output.c:293
High Level API.
@ pcmk_sim_verbose
Definition pacemaker.h:44
@ pcmk_sim_sanitized
Definition pacemaker.h:43
@ pcmk_sim_show_scores
Definition pacemaker.h:40
@ pcmk_sim_simulate
Definition pacemaker.h:42
@ pcmk_sim_process
Definition pacemaker.h:39
@ pcmk_sim_show_pending
Definition pacemaker.h:38
@ pcmk_sim_all_actions
Definition pacemaker.h:37
@ pcmk_sim_show_utilization
Definition pacemaker.h:41
const char * action
Definition pcmk_fence.c:32
const char * target
Definition pcmk_fence.c:31
int pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out, const pcmk_injections_t *injections, uint32_t flags, uint32_t section_opts, const char *use_date, const char *input_file, const char *graph_file, const char *dot_file)
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's response to events.
enum pcmk__graph_status pcmk__simulate_transition(pcmk_scheduler_t *scheduler, cib_t *cib, const GList *op_fail_list)
int pcmk__profile_dir(pcmk__output_t *out, uint32_t flags, const char *dir, unsigned int repeat, const char *use_date)
@ pcmk__fence_history_none
Definition pcmki_fence.h:23
void pcmk__register_lib_messages(pcmk__output_t *out)
void pcmk__schedule_actions(pcmk_scheduler_t *scheduler)
@ pcmk__graph_action_confirmed
@ pcmk__graph_action_failed
void pcmk__free_graph(pcmk__graph_t *graph)
pcmk__graph_t * pcmk__unpack_graph(const xmlNode *xml_graph, const char *reference)
void pcmk__set_graph_functions(pcmk__graph_functions_t *fns)
const char * pcmk__graph_status2text(enum pcmk__graph_status state)
enum pcmk__graph_status pcmk__execute_graph(pcmk__graph_t *graph)
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)
pcmk__graph_status
@ pcmk__graph_active
@ pcmk__graph_complete
void pcmk__update_graph(pcmk__graph_t *graph, const pcmk__graph_action_t *action)
void pcmk__log_graph(unsigned int log_level, pcmk__graph_t *graph)
#define pcmk__set_graph_action_flags(action, flags_to_set)
void pe__register_messages(pcmk__output_t *out)
Definition pe_output.c:3482
@ pcmk__rsc_managed
Function and executable result codes.
@ CRM_EX_OK
Success.
Definition results.h:233
@ PCMK_OCF_OK
Success.
Definition results.h:174
@ pcmk_rc_no_output
Definition results.h:128
@ pcmk_rc_ok
Definition results.h:159
@ pcmk_rc_dot_error
Definition results.h:125
@ pcmk_rc_invalid_transition
Definition results.h:123
@ pcmk_rc_graph_error
Definition results.h:124
#define pcmk_ok
Definition results.h:65
@ PCMK_EXEC_DONE
Action completed, result is known.
Definition results.h:311
int pcmk_legacy2rc(int legacy_rc)
Definition results.c:675
enum crm_exit_e crm_exit_t
Exit status codes for tools and daemons.
crm_exit_t pcmk_rc2exitc(int rc)
Map a function return code to the most similar exit code.
Definition results.c:820
#define pcmk__assert(expr)
Scheduler API.
void pcmk_free_scheduler(pcmk_scheduler_t *scheduler)
Free scheduler data.
Definition scheduler.c:193
pcmk_scheduler_t * pcmk_new_scheduler(void)
Create a new object to hold scheduler data.
Definition scheduler.c:32
void pcmk_reset_scheduler(pcmk_scheduler_t *scheduler)
Reset scheduler data to defaults.
Definition scheduler.c:87
#define pcmk__set_scheduler_flags(scheduler, flags_to_set)
@ pcmk__sched_in_maintenance
@ pcmk__sched_sanitized
@ pcmk__sched_show_utilization
@ pcmk__sched_output_scores
@ pcmk__sched_none
bool pcmk__validate_xml(xmlNode *xml_blob, const char *validation, xmlRelaxNGValidityErrorFunc error_handler, void *error_handler_context)
Definition schemas.c:825
int pcmk__update_configured_schema(xmlNode **xml, bool to_logs)
Update XML from its configured schema to the latest major series.
Definition schemas.c:1257
bool pcmk_str_is_infinity(const char *s)
Definition scores.c:130
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:26
pcmk_resource_t * pe_find_resource(GList *rsc_list, const char *id)
Definition status.c:191
gboolean cluster_status(pcmk_scheduler_t *scheduler)
Definition status.c:73
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:116
bool pcmk__ends_with_ext(const char *s, const char *match)
Definition strings.c:637
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1029
@ pcmk__str_none
@ pcmk__str_casei
int pcmk__guint_from_hash(GHashTable *table, const char *key, guint default_val, guint *result)
Definition strings.c:303
bool pcmk__str_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1053
void pcmk__g_strcat(GString *buffer,...) G_GNUC_NULL_TERMINATED
Definition strings.c:1299
int(* remove)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition cib_types.h:174
int(* query)(cib_t *cib, const char *section, xmlNode **output_data, int call_options)
Definition cib_types.h:151
int(* replace)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition cib_types.h:172
int(* modify)(cib_t *cib, const char *section, xmlNode *data, int call_options)
Definition cib_types.h:169
cib_api_operations_t * cmds
Definition cib_types.h:325
const char * op_type
Definition lrmd_events.h:45
enum ocf_exitcode rc
Definition lrmd_events.h:65
char * failed_stop_offset
Failcount after one failed stop action.
char * failed_start_offset
Failcount after one failed start action.
This structure contains everything that makes up a single output formatter.
void(* end_list)(pcmk__output_t *out)
int(* message)(pcmk__output_t *out, const char *message_id,...)
bool(* is_quiet)(pcmk__output_t *out)
int int void(* err)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
void(* finish)(pcmk__output_t *out, crm_exit_t exit_status, bool print, void **copy_dest)
void(* begin_list)(pcmk__output_t *out, const char *singular_noun, const char *plural_noun, const char *format,...) G_GNUC_PRINTF(4
int(* info)(pcmk__output_t *out, const char *format,...) G_GNUC_PRINTF(2
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
xmlNode * input
Definition scheduler.h:81
uint64_t flags
Definition scheduler.h:89
Synthetic cluster events that can be injected into the cluster for running simulations.
Definition pacemaker.h:51
GList * node_down
Definition pacemaker.h:55
GList * ticket_activate
Definition pacemaker.h:75
GList * ticket_grant
Definition pacemaker.h:69
GList * ticket_revoke
Definition pacemaker.h:71
GList * node_fail
Definition pacemaker.h:57
GList * op_inject
Definition pacemaker.h:62
GList * ticket_standby
Definition pacemaker.h:73
const char * crm_element_value(const xmlNode *data, const char *name)
Retrieve the value of an XML attribute.
char * crm_element_value_copy(const xmlNode *data, const char *name)
Retrieve a copy of the value of an XML attribute.
int crm_element_value_epoch(const xmlNode *xml, const char *name, time_t *dest)
Retrieve the seconds-since-epoch value of an XML attribute.
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Create an XML attribute with specified name and value.
xmlNode * pcmk__xe_first_child(const xmlNode *parent, const char *node_name, const char *attr_n, const char *attr_v)
Definition xml_element.c:43
xmlNode * pcmk__xe_create(xmlNode *parent, const char *name)
xmlNode * pcmk__xml_copy(xmlNode *parent, xmlNode *src)
Definition xml.c:832
void pcmk__xml_free(xmlNode *xml)
Definition xml.c:816
xmlNode * pcmk__xml_read(const char *filename)
Definition xml_io.c:101
int pcmk__xml_write_file(const xmlNode *xml, const char *filename, bool compress)
Definition xml_io.c:560
#define PCMK_XE_STATUS
Definition xml_names.h:204
#define PCMK_XA_CLASS
Definition xml_names.h:246
#define PCMK_XA_EXECUTION_DATE
Definition xml_names.h:272
#define PCMK_XA_OPERATION
Definition xml_names.h:349
#define PCMK_XA_ID
Definition xml_names.h:301
#define PCMK_XA_PROVIDER
Definition xml_names.h:364
#define PCMK_XE_PRIMITIVE
Definition xml_names.h:164
#define PCMK_XA_TYPE
Definition xml_names.h:430
#define PCMK_XA_CRM_DEBUG_ORIGIN
Definition xml_names.h:253
#define PCMK_XA_UNAME
Definition xml_names.h:431
#define PCMK__XE_TRANSIENT_ATTRIBUTES
#define PCMK__XA_ROUTER_NODE
#define PCMK__XA_OPERATION_KEY
#define PCMK__XE_NODE_STATE
#define PCMK__XE_LRM
#define PCMK__XA_LONG_ID