This source file includes following definitions.
- create_action_name
- print_cluster_status
- print_transition_summary
- reset
- write_sim_dotfile
- profile_file
- pcmk__profile_dir
- set_effective_date
- simulate_pseudo_action
- simulate_resource_action
- simulate_cluster_action
- simulate_fencing_action
- pcmk__simulate_transition
- pcmk__simulate
- pcmk_simulate
1
2
3
4
5
6
7
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
33
34
35
36
37
38
39
40
41
42
43 static char *
44 create_action_name(const pcmk_action_t *action, bool verbose)
45 {
46 char *action_name = NULL;
47 const char *prefix = "";
48 const char *action_host = NULL;
49 const char *history_id = NULL;
50 const char *task = action->task;
51
52 if (action->node != NULL) {
53 action_host = action->node->priv->name;
54 } else if (!pcmk_is_set(action->flags, pcmk__action_pseudo)) {
55 action_host = "<none>";
56 }
57
58 if (pcmk__str_eq(action->task, PCMK_ACTION_CANCEL, pcmk__str_none)) {
59 prefix = "Cancel ";
60 task = action->cancel_task;
61 }
62
63 if (action->rsc != NULL) {
64 history_id = action->rsc->priv->history_id;
65 }
66
67 if (history_id != NULL) {
68 char *key = NULL;
69 guint interval_ms = 0;
70
71 if (pcmk__guint_from_hash(action->meta, PCMK_META_INTERVAL, 0,
72 &interval_ms) != pcmk_rc_ok) {
73 interval_ms = 0;
74 }
75
76 if (pcmk__strcase_any_of(action->task, PCMK_ACTION_NOTIFY,
77 PCMK_ACTION_NOTIFIED, NULL)) {
78 const char *n_type = g_hash_table_lookup(action->meta,
79 "notify_key_type");
80 const char *n_task = g_hash_table_lookup(action->meta,
81 "notify_key_operation");
82
83 pcmk__assert((n_type != NULL) && (n_task != NULL));
84 key = pcmk__notify_key(history_id, n_type, n_task);
85 } else {
86 key = pcmk__op_key(history_id, task, interval_ms);
87 }
88
89 if (action_host != NULL) {
90 action_name = crm_strdup_printf("%s%s %s",
91 prefix, key, action_host);
92 } else {
93 action_name = crm_strdup_printf("%s%s", prefix, key);
94 }
95 free(key);
96
97 } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
98 pcmk__str_none)) {
99 const char *op = g_hash_table_lookup(action->meta,
100 PCMK__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
126
127
128
129
130
131
132
133
134
135
136 static void
137 print_cluster_status(pcmk_scheduler_t *scheduler, uint32_t show_opts,
138 uint32_t section_opts, const char *title,
139 bool print_spacer)
140 {
141 pcmk__output_t *out = scheduler->priv->out;
142 GList *all = NULL;
143 crm_exit_t stonith_rc = 0;
144 enum pcmk_pacemakerd_state state = pcmk_pacemakerd_state_invalid;
145
146 section_opts |= pcmk_section_nodes | pcmk_section_resources;
147 show_opts |= pcmk_show_inactive_rscs | pcmk_show_failed_detail;
148
149 all = g_list_prepend(all, (gpointer) "*");
150
151 PCMK__OUTPUT_SPACER_IF(out, print_spacer);
152 out->begin_list(out, NULL, NULL, "%s", title);
153 out->message(out, "cluster-status",
154 scheduler, state, stonith_rc, NULL,
155 pcmk__fence_history_none, section_opts, show_opts, NULL,
156 all, all);
157 out->end_list(out);
158
159 g_list_free(all);
160 }
161
162
163
164
165
166
167
168
169 static void
170 print_transition_summary(pcmk_scheduler_t *scheduler, bool print_spacer)
171 {
172 pcmk__output_t *out = scheduler->priv->out;
173
174 PCMK__OUTPUT_SPACER_IF(out, print_spacer);
175 out->begin_list(out, NULL, NULL, "Transition Summary");
176 pcmk__output_actions(scheduler);
177 out->end_list(out);
178 }
179
180
181
182
183
184
185
186
187
188
189
190 static void
191 reset(pcmk_scheduler_t *scheduler, xmlNodePtr input, pcmk__output_t *out,
192 const char *use_date, unsigned int flags)
193 {
194 scheduler->input = input;
195 scheduler->priv->out = out;
196 set_effective_date(scheduler, true, use_date);
197 if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
198 pcmk__set_scheduler_flags(scheduler, pcmk__sched_sanitized);
199 }
200 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
201 pcmk__set_scheduler_flags(scheduler, pcmk__sched_output_scores);
202 }
203 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
204 pcmk__set_scheduler_flags(scheduler, pcmk__sched_show_utilization);
205 }
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 static int
222 write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file,
223 bool all_actions, bool verbose)
224 {
225 GList *iter = NULL;
226 FILE *dot_strm = fopen(dot_file, "w");
227
228 if (dot_strm == NULL) {
229 return errno;
230 }
231
232 fprintf(dot_strm, " digraph \"g\" {\n");
233 for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
234 pcmk_action_t *action = (pcmk_action_t *) iter->data;
235 const char *style = "dashed";
236 const char *font = "black";
237 const char *color = "black";
238 char *action_name = create_action_name(action, verbose);
239
240 if (pcmk_is_set(action->flags, pcmk__action_pseudo)) {
241 font = "orange";
242 }
243
244 if (pcmk_is_set(action->flags, pcmk__action_added_to_graph)) {
245 style = PCMK__VALUE_BOLD;
246 color = "green";
247
248 } else if ((action->rsc != NULL)
249 && !pcmk_is_set(action->rsc->flags, pcmk__rsc_managed)) {
250 color = "red";
251 font = "purple";
252 if (!all_actions) {
253 goto do_not_write;
254 }
255
256 } else if (pcmk_is_set(action->flags, pcmk__action_optional)) {
257 color = "blue";
258 if (!all_actions) {
259 goto do_not_write;
260 }
261
262 } else {
263 color = "red";
264 CRM_LOG_ASSERT(!pcmk_is_set(action->flags, pcmk__action_runnable));
265 }
266
267 pcmk__set_action_flags(action, pcmk__action_added_to_graph);
268 fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
269 action_name, style, color, font);
270 do_not_write:
271 free(action_name);
272 }
273
274 for (iter = scheduler->priv->actions; iter != NULL; iter = iter->next) {
275 pcmk_action_t *action = (pcmk_action_t *) iter->data;
276
277 for (GList *before_iter = action->actions_before;
278 before_iter != NULL; before_iter = before_iter->next) {
279
280 pcmk__related_action_t *before = before_iter->data;
281
282 char *before_name = NULL;
283 char *after_name = NULL;
284 const char *style = "dashed";
285 bool optional = true;
286
287 if (before->graphed) {
288 optional = false;
289 style = PCMK__VALUE_BOLD;
290 } else if (before->flags == pcmk__ar_none) {
291 continue;
292 } else if (pcmk_is_set(before->action->flags,
293 pcmk__action_added_to_graph)
294 && pcmk_is_set(action->flags, pcmk__action_added_to_graph)
295 && before->flags != 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
316
317
318
319
320
321
322
323
324
325
326
327
328 static void
329 profile_file(const char *xml_file, long long repeat,
330 pcmk_scheduler_t *scheduler, const char *use_date)
331 {
332 pcmk__output_t *out = scheduler->priv->out;
333 xmlNode *cib_object = NULL;
334 clock_t start = 0;
335 clock_t end;
336 unsigned long long scheduler_flags = pcmk__sched_none;
337
338 pcmk__assert(out != NULL);
339
340 cib_object = pcmk__xml_read(xml_file);
341 start = clock();
342
343 if (pcmk_find_cib_element(cib_object, PCMK_XE_STATUS) == NULL) {
344 pcmk__xe_create(cib_object, PCMK_XE_STATUS);
345 }
346
347 if (pcmk__update_configured_schema(&cib_object, false) != pcmk_rc_ok) {
348 pcmk__xml_free(cib_object);
349 return;
350 }
351
352 if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) {
353 pcmk__xml_free(cib_object);
354 return;
355 }
356
357 if (pcmk_is_set(scheduler->flags, pcmk__sched_output_scores)) {
358 scheduler_flags |= pcmk__sched_output_scores;
359 }
360 if (pcmk_is_set(scheduler->flags, pcmk__sched_show_utilization)) {
361 scheduler_flags |= pcmk__sched_show_utilization;
362 }
363
364 for (int i = 0; i < repeat; ++i) {
365 xmlNode *input = cib_object;
366
367 if (repeat > 1) {
368 input = pcmk__xml_copy(NULL, cib_object);
369 }
370 scheduler->input = input;
371 set_effective_date(scheduler, false, use_date);
372 pcmk__schedule_actions(input, scheduler_flags, scheduler);
373 pe_reset_working_set(scheduler);
374 }
375
376 end = clock();
377 out->message(out, "profile", xml_file, start, end);
378 }
379
380 void
381 pcmk__profile_dir(const char *dir, long long repeat,
382 pcmk_scheduler_t *scheduler, const char *use_date)
383 {
384 pcmk__output_t *out = scheduler->priv->out;
385 struct dirent **namelist;
386
387 int file_num = scandir(dir, &namelist, 0, alphasort);
388
389 pcmk__assert(out != NULL);
390
391 if (file_num > 0) {
392 struct stat prop;
393 char buffer[FILENAME_MAX];
394
395 out->begin_list(out, NULL, NULL, "Timings");
396
397 while (file_num--) {
398 if ('.' == namelist[file_num]->d_name[0]) {
399 free(namelist[file_num]);
400 continue;
401
402 } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
403 ".xml")) {
404 free(namelist[file_num]);
405 continue;
406 }
407 snprintf(buffer, sizeof(buffer), "%s/%s",
408 dir, namelist[file_num]->d_name);
409 if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
410 profile_file(buffer, repeat, scheduler, use_date);
411 }
412 free(namelist[file_num]);
413 }
414 free(namelist);
415
416 out->end_list(out);
417 }
418 }
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433 static void
434 set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
435 const char *use_date)
436 {
437 pcmk__output_t *out = scheduler->priv->out;
438 time_t original_date = 0;
439
440 pcmk__assert(out != NULL);
441
442 crm_element_value_epoch(scheduler->input, PCMK_XA_EXECUTION_DATE,
443 &original_date);
444
445 if (use_date) {
446 scheduler->priv->now = crm_time_new(use_date);
447 out->info(out, "Setting effective cluster time: %s", use_date);
448 crm_time_log(LOG_NOTICE, "Pretending 'now' is", scheduler->priv->now,
449 crm_time_log_date | crm_time_log_timeofday);
450
451 } else if (original_date != 0) {
452 scheduler->priv->now = pcmk__copy_timet(original_date);
453
454 if (print_original) {
455 char *when = crm_time_as_string(scheduler->priv->now,
456 crm_time_log_date
457 |crm_time_log_timeofday);
458
459 out->info(out, "Using the original execution date of: %s", when);
460 free(when);
461 }
462 }
463 }
464
465
466
467
468
469
470
471
472
473
474 static int
475 simulate_pseudo_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
476 {
477 const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE);
478 const char *task = crm_element_value(action->xml, PCMK__XA_OPERATION_KEY);
479
480 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
481 out->message(out, "inject-pseudo-action", node, task);
482
483 pcmk__update_graph(graph, action);
484 return pcmk_rc_ok;
485 }
486
487
488
489
490
491
492
493
494
495
496 static int
497 simulate_resource_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
498 {
499 int rc;
500 lrmd_event_data_t *op = NULL;
501 int target_outcome = PCMK_OCF_OK;
502
503 const char *rtype = NULL;
504 const char *rclass = NULL;
505 const char *resource = NULL;
506 const char *rprovider = NULL;
507 const char *resource_config_name = NULL;
508 const char *operation = crm_element_value(action->xml, PCMK_XA_OPERATION);
509 const char *target_rc_s = crm_meta_value(action->params,
510 PCMK__META_OP_TARGET_RC);
511
512 xmlNode *cib_node = NULL;
513 xmlNode *cib_resource = NULL;
514 xmlNode *action_rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE,
515 NULL, NULL);
516
517 char *node = crm_element_value_copy(action->xml, PCMK__META_ON_NODE);
518 char *uuid = NULL;
519 const char *router_node = crm_element_value(action->xml,
520 PCMK__XA_ROUTER_NODE);
521
522
523 if (pcmk__str_eq(operation, CRM_OP_REPROBE, pcmk__str_none)) {
524 crm_debug("No history injection for %s op on %s", operation, node);
525 goto done;
526 }
527
528 if (action_rsc == NULL) {
529 crm_log_xml_err(action->xml, "Bad");
530 free(node);
531 return EPROTO;
532 }
533
534
535
536
537
538
539 resource_config_name = crm_element_value(action_rsc, PCMK_XA_ID);
540 if (resource_config_name == NULL) {
541 crm_log_xml_err(action->xml, "No ID");
542 free(node);
543 return EPROTO;
544 }
545 resource = resource_config_name;
546 if (pe_find_resource(fake_resource_list, resource) == NULL) {
547 const char *longname = crm_element_value(action_rsc, PCMK__XA_LONG_ID);
548
549 if ((longname != NULL)
550 && (pe_find_resource(fake_resource_list, longname) != NULL)) {
551 resource = longname;
552 }
553 }
554
555
556 if (pcmk__strcase_any_of(operation, PCMK_ACTION_DELETE,
557 PCMK_ACTION_META_DATA, NULL)) {
558 out->message(out, "inject-rsc-action", resource, operation, node,
559 (guint) 0);
560 goto done;
561 }
562
563 rclass = crm_element_value(action_rsc, PCMK_XA_CLASS);
564 rtype = crm_element_value(action_rsc, PCMK_XA_TYPE);
565 rprovider = crm_element_value(action_rsc, PCMK_XA_PROVIDER);
566
567 pcmk__scan_min_int(target_rc_s, &target_outcome, 0);
568
569 pcmk__assert(fake_cib->cmds->query(fake_cib, NULL, NULL,
570 cib_sync_call) == pcmk_ok);
571
572
573 uuid = crm_element_value_copy(action->xml, PCMK__META_ON_NODE_UUID);
574 cib_node = pcmk__inject_node(fake_cib, node,
575 ((router_node == NULL)? uuid: node));
576 free(uuid);
577 pcmk__assert(cib_node != NULL);
578
579
580 cib_resource = pcmk__inject_resource_history(out, cib_node, resource,
581 resource_config_name,
582 rclass, rtype, rprovider);
583 if (cib_resource == NULL) {
584 crm_err("Could not simulate action %d history for resource %s",
585 action->id, resource);
586 free(node);
587 pcmk__xml_free(cib_node);
588 return EINVAL;
589 }
590
591
592 op = pcmk__event_from_graph_action(cib_resource, action, PCMK_EXEC_DONE,
593 target_outcome, "User-injected result");
594 out->message(out, "inject-rsc-action", resource, op->op_type, node,
595 op->interval_ms);
596
597
598 for (const GList *iter = fake_op_fail_list;
599 iter != NULL; iter = iter->next) {
600 const char *spec = (const char *) iter->data;
601 char *key = NULL;
602 const char *match_name = NULL;
603 const char *offset = NULL;
604
605
606 key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
607 op->interval_ms, node);
608 if (strncasecmp(key, spec, strlen(key)) == 0) {
609 match_name = resource;
610 }
611 free(key);
612
613
614 if ((match_name == NULL)
615 && (strcmp(resource, resource_config_name) != 0)) {
616
617 key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
618 op->op_type, op->interval_ms, node);
619 if (strncasecmp(key, spec, strlen(key)) == 0) {
620 match_name = resource_config_name;
621 }
622 free(key);
623 }
624
625 if (match_name == NULL) {
626 continue;
627 }
628
629
630 rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
631 if (rc != 1) {
632 out->err(out, "Invalid failed operation '%s' "
633 "(result code must be integer)", spec);
634 continue;
635 }
636
637 out->info(out, "Pretending action %d failed with rc=%d",
638 action->id, op->rc);
639 pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
640 graph->abort_priority = PCMK_SCORE_INFINITY;
641
642 if (pcmk__str_eq(op->op_type, PCMK_ACTION_START, pcmk__str_none)) {
643 offset = pcmk__s(graph->failed_start_offset, PCMK_VALUE_INFINITY);
644
645 } else if (pcmk__str_eq(op->op_type, PCMK_ACTION_STOP,
646 pcmk__str_none)) {
647 offset = pcmk__s(graph->failed_stop_offset, PCMK_VALUE_INFINITY);
648 }
649
650 pcmk__inject_failcount(out, fake_cib, cib_node, match_name, op->op_type,
651 op->interval_ms, op->rc,
652 pcmk_str_is_infinity(offset));
653 break;
654 }
655
656 pcmk__inject_action_result(cib_resource, op, node, target_outcome);
657 lrmd_free_event(op);
658 rc = fake_cib->cmds->modify(fake_cib, PCMK_XE_STATUS, cib_node,
659 cib_sync_call);
660 pcmk__assert(rc == pcmk_ok);
661
662 done:
663 free(node);
664 pcmk__xml_free(cib_node);
665 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
666 pcmk__update_graph(graph, action);
667 return pcmk_rc_ok;
668 }
669
670
671
672
673
674
675
676
677
678
679 static int
680 simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
681 {
682 const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE);
683 const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
684 xmlNode *rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
685 NULL);
686
687 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
688 out->message(out, "inject-cluster-action", node, task, rsc);
689 pcmk__update_graph(graph, action);
690 return pcmk_rc_ok;
691 }
692
693
694
695
696
697
698
699
700
701
702 static int
703 simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
704 {
705 const char *op = crm_meta_value(action->params, PCMK__META_STONITH_ACTION);
706 char *target = crm_element_value_copy(action->xml, PCMK__META_ON_NODE);
707
708 out->message(out, "inject-fencing-action", target, op);
709
710 if (!pcmk__str_eq(op, PCMK_ACTION_ON, pcmk__str_casei)) {
711 int rc = pcmk_ok;
712 GString *xpath = g_string_sized_new(512);
713
714
715 xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
716 false);
717
718 pcmk__assert(cib_node != NULL);
719 crm_xml_add(cib_node, PCMK_XA_CRM_DEBUG_ORIGIN, __func__);
720 rc = fake_cib->cmds->replace(fake_cib, PCMK_XE_STATUS, cib_node,
721 cib_sync_call);
722 pcmk__assert(rc == pcmk_ok);
723
724
725 pcmk__g_strcat(xpath,
726 "//" PCMK__XE_NODE_STATE
727 "[@" PCMK_XA_UNAME "='", target, "']/" PCMK__XE_LRM,
728 NULL);
729 fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
730 cib_xpath|cib_sync_call);
731
732 g_string_truncate(xpath, 0);
733 pcmk__g_strcat(xpath,
734 "//" PCMK__XE_NODE_STATE
735 "[@" PCMK_XA_UNAME "='", target, "']"
736 "/" PCMK__XE_TRANSIENT_ATTRIBUTES, NULL);
737 fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
738 cib_xpath|cib_sync_call);
739
740 pcmk__xml_free(cib_node);
741 g_string_free(xpath, TRUE);
742 }
743
744 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
745 pcmk__update_graph(graph, action);
746 free(target);
747 return pcmk_rc_ok;
748 }
749
750 enum pcmk__graph_status
751 pcmk__simulate_transition(pcmk_scheduler_t *scheduler, cib_t *cib,
752 const GList *op_fail_list)
753 {
754 pcmk__graph_t *transition = NULL;
755 enum pcmk__graph_status graph_rc;
756
757 pcmk__graph_functions_t simulation_fns = {
758 simulate_pseudo_action,
759 simulate_resource_action,
760 simulate_cluster_action,
761 simulate_fencing_action,
762 };
763
764 out = scheduler->priv->out;
765
766 fake_cib = cib;
767 fake_op_fail_list = op_fail_list;
768
769 if (!out->is_quiet(out)) {
770 out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
771 }
772
773 pcmk__set_graph_functions(&simulation_fns);
774 transition = pcmk__unpack_graph(scheduler->priv->graph, crm_system_name);
775 pcmk__log_graph(LOG_DEBUG, transition);
776
777 fake_resource_list = scheduler->priv->resources;
778 do {
779 graph_rc = pcmk__execute_graph(transition);
780 } while (graph_rc == pcmk__graph_active);
781 fake_resource_list = NULL;
782
783 if (graph_rc != pcmk__graph_complete) {
784 out->err(out, "Transition failed: %s",
785 pcmk__graph_status2text(graph_rc));
786 pcmk__log_graph(LOG_ERR, transition);
787 out->err(out, "An invalid transition was produced");
788 }
789 pcmk__free_graph(transition);
790
791 if (!out->is_quiet(out)) {
792
793 xmlNode *cib_object = NULL;
794 int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
795 cib_sync_call);
796
797 pcmk__assert(rc == pcmk_ok);
798 pe_reset_working_set(scheduler);
799 scheduler->input = cib_object;
800 out->end_list(out);
801 }
802 return graph_rc;
803 }
804
805 int
806 pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out,
807 const pcmk_injections_t *injections, unsigned int flags,
808 uint32_t section_opts, const char *use_date,
809 const char *input_file, const char *graph_file,
810 const char *dot_file)
811 {
812 int printed = pcmk_rc_no_output;
813 int rc = pcmk_rc_ok;
814 xmlNodePtr input = NULL;
815 cib_t *cib = NULL;
816
817 rc = cib__signon_query(out, &cib, &input);
818 if (rc != pcmk_rc_ok) {
819 goto simulate_done;
820 }
821
822 reset(scheduler, input, out, use_date, flags);
823 cluster_status(scheduler);
824
825 if (!out->is_quiet(out)) {
826 const bool show_pending = pcmk_is_set(flags, pcmk_sim_show_pending);
827
828 if (pcmk_is_set(scheduler->flags, pcmk__sched_in_maintenance)) {
829 printed = out->message(out, "maint-mode", scheduler->flags);
830 }
831
832 if ((scheduler->priv->disabled_resources > 0)
833 || (scheduler->priv->blocked_resources > 0)) {
834
835 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
836 printed = out->info(out,
837 "%d of %d resource instances DISABLED and "
838 "%d BLOCKED from further action due to failure",
839 scheduler->priv->disabled_resources,
840 scheduler->priv->ninstances,
841 scheduler->priv->blocked_resources);
842 }
843
844
845
846
847 print_cluster_status(scheduler, (show_pending? pcmk_show_pending : 0),
848 section_opts, "Current cluster status",
849 (printed == pcmk_rc_ok));
850 printed = pcmk_rc_ok;
851 }
852
853
854 if ((injections->node_down != NULL)
855 || (injections->node_fail != NULL)
856 || (injections->node_up != NULL)
857 || (injections->op_inject != NULL)
858 || (injections->ticket_activate != NULL)
859 || (injections->ticket_grant != NULL)
860 || (injections->ticket_revoke != NULL)
861 || (injections->ticket_standby != NULL)
862 || (injections->watchdog != NULL)) {
863
864 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
865 pcmk__inject_scheduler_input(scheduler, cib, injections);
866 printed = pcmk_rc_ok;
867
868 rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
869 if (rc != pcmk_rc_ok) {
870 rc = pcmk_legacy2rc(rc);
871 goto simulate_done;
872 }
873
874 cleanup_calculations(scheduler);
875 reset(scheduler, input, out, use_date, flags);
876 cluster_status(scheduler);
877 }
878
879 if (input_file != NULL) {
880 rc = pcmk__xml_write_file(input, input_file, false);
881 if (rc != pcmk_rc_ok) {
882 goto simulate_done;
883 }
884 }
885
886 if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
887 pcmk__output_t *logger_out = NULL;
888 unsigned long long scheduler_flags = pcmk__sched_none;
889
890 if (pcmk_is_set(scheduler->flags, pcmk__sched_output_scores)) {
891 scheduler_flags |= pcmk__sched_output_scores;
892 }
893 if (pcmk_is_set(scheduler->flags, pcmk__sched_show_utilization)) {
894 scheduler_flags |= pcmk__sched_show_utilization;
895 }
896
897 if (pcmk_all_flags_set(scheduler->flags,
898 pcmk__sched_output_scores
899 |pcmk__sched_show_utilization)) {
900 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
901 out->begin_list(out, NULL, NULL,
902 "Assignment Scores and Utilization Information");
903 printed = pcmk_rc_ok;
904
905 } else if (pcmk_is_set(scheduler->flags, pcmk__sched_output_scores)) {
906 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
907 out->begin_list(out, NULL, NULL, "Assignment Scores");
908 printed = pcmk_rc_ok;
909
910 } else if (pcmk_is_set(scheduler->flags, pcmk__sched_show_utilization)) {
911 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
912 out->begin_list(out, NULL, NULL, "Utilization Information");
913 printed = pcmk_rc_ok;
914
915 } else {
916 rc = pcmk__log_output_new(&logger_out);
917 if (rc != pcmk_rc_ok) {
918 goto simulate_done;
919 }
920 pe__register_messages(logger_out);
921 pcmk__register_lib_messages(logger_out);
922 scheduler->priv->out = logger_out;
923 }
924
925 pcmk__schedule_actions(input, scheduler_flags, scheduler);
926
927 if (logger_out == NULL) {
928 out->end_list(out);
929 } else {
930 logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
931 pcmk__output_free(logger_out);
932 scheduler->priv->out = out;
933 }
934
935 input = NULL;
936
937 if (graph_file != NULL) {
938 rc = pcmk__xml_write_file(scheduler->priv->graph, graph_file,
939 false);
940 if (rc != pcmk_rc_ok) {
941 rc = pcmk_rc_graph_error;
942 goto simulate_done;
943 }
944 }
945
946 if (dot_file != NULL) {
947 rc = write_sim_dotfile(scheduler, dot_file,
948 pcmk_is_set(flags, pcmk_sim_all_actions),
949 pcmk_is_set(flags, pcmk_sim_verbose));
950 if (rc != pcmk_rc_ok) {
951 rc = pcmk_rc_dot_error;
952 goto simulate_done;
953 }
954 }
955
956 if (!out->is_quiet(out)) {
957 print_transition_summary(scheduler, printed == pcmk_rc_ok);
958 }
959 }
960
961 rc = pcmk_rc_ok;
962
963 if (!pcmk_is_set(flags, pcmk_sim_simulate)) {
964 goto simulate_done;
965 }
966
967 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
968 if (pcmk__simulate_transition(scheduler, cib, injections->op_fail)
969 != pcmk__graph_complete) {
970 rc = pcmk_rc_invalid_transition;
971 }
972
973 if (out->is_quiet(out)) {
974 goto simulate_done;
975 }
976
977 set_effective_date(scheduler, true, use_date);
978
979 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
980 pcmk__set_scheduler_flags(scheduler, pcmk__sched_output_scores);
981 }
982 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
983 pcmk__set_scheduler_flags(scheduler, pcmk__sched_show_utilization);
984 }
985
986 cluster_status(scheduler);
987 print_cluster_status(scheduler, 0, section_opts, "Revised Cluster Status",
988 true);
989
990 simulate_done:
991 cib__clean_up_connection(&cib);
992 return rc;
993 }
994
995 int
996 pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler,
997 const pcmk_injections_t *injections, unsigned int flags,
998 unsigned int section_opts, const char *use_date,
999 const char *input_file, const char *graph_file,
1000 const char *dot_file)
1001 {
1002 pcmk__output_t *out = NULL;
1003 int rc = pcmk_rc_ok;
1004
1005 rc = pcmk__xml_output_new(&out, xml);
1006 if (rc != pcmk_rc_ok) {
1007 return rc;
1008 }
1009
1010 pe__register_messages(out);
1011 pcmk__register_lib_messages(out);
1012
1013 rc = pcmk__simulate(scheduler, out, injections, flags, section_opts,
1014 use_date, input_file, graph_file, dot_file);
1015 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
1016 return rc;
1017 }