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 *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, 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);
84 pcmk__assert(n_task != NULL);
85 key = pcmk__notify_key(clone_name, n_type, n_task);
86 } else {
87 key = pcmk__op_key(clone_name, 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,
99 pcmk__str_none)) {
100 const char *op = g_hash_table_lookup(action->meta,
101 PCMK__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
127
128
129
130
131
132
133
134
135
136
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;
145 enum pcmk_pacemakerd_state state = pcmk_pacemakerd_state_invalid;
146
147 section_opts |= pcmk_section_nodes | pcmk_section_resources;
148 show_opts |= pcmk_show_inactive_rscs | pcmk_show_failed_detail;
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
163
164
165
166
167
168
169
170 static void
171 print_transition_summary(pcmk_scheduler_t *scheduler, bool print_spacer)
172 {
173 pcmk__output_t *out = scheduler->priv;
174
175 PCMK__OUTPUT_SPACER_IF(out, print_spacer);
176 out->begin_list(out, NULL, NULL, "Transition Summary");
177 pcmk__output_actions(scheduler);
178 out->end_list(out);
179 }
180
181
182
183
184
185
186
187
188
189
190
191 static void
192 reset(pcmk_scheduler_t *scheduler, xmlNodePtr input, pcmk__output_t *out,
193 const char *use_date, unsigned int flags)
194 {
195 scheduler->input = input;
196 scheduler->priv = out;
197 set_effective_date(scheduler, true, use_date);
198 if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
199 pcmk__set_scheduler_flags(scheduler, pcmk_sched_sanitized);
200 }
201 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
202 pcmk__set_scheduler_flags(scheduler, pcmk_sched_output_scores);
203 }
204 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
205 pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization);
206 }
207 }
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222 static int
223 write_sim_dotfile(pcmk_scheduler_t *scheduler, const char *dot_file,
224 bool all_actions, bool verbose)
225 {
226 GList *iter = NULL;
227 FILE *dot_strm = fopen(dot_file, "w");
228
229 if (dot_strm == NULL) {
230 return errno;
231 }
232
233 fprintf(dot_strm, " digraph \"g\" {\n");
234 for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
235 pcmk_action_t *action = (pcmk_action_t *) iter->data;
236 const char *style = "dashed";
237 const char *font = "black";
238 const char *color = "black";
239 char *action_name = create_action_name(action, verbose);
240
241 if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
242 font = "orange";
243 }
244
245 if (pcmk_is_set(action->flags, pcmk_action_added_to_graph)) {
246 style = PCMK__VALUE_BOLD;
247 color = "green";
248
249 } else if ((action->rsc != NULL)
250 && !pcmk_is_set(action->rsc->flags, pcmk_rsc_managed)) {
251 color = "red";
252 font = "purple";
253 if (!all_actions) {
254 goto do_not_write;
255 }
256
257 } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
258 color = "blue";
259 if (!all_actions) {
260 goto do_not_write;
261 }
262
263 } else {
264 color = "red";
265 CRM_LOG_ASSERT(!pcmk_is_set(action->flags, pcmk_action_runnable));
266 }
267
268 pcmk__set_action_flags(action, pcmk_action_added_to_graph);
269 fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
270 action_name, style, color, font);
271 do_not_write:
272 free(action_name);
273 }
274
275 for (iter = scheduler->actions; iter != NULL; iter = iter->next) {
276 pcmk_action_t *action = (pcmk_action_t *) iter->data;
277
278 for (GList *before_iter = action->actions_before;
279 before_iter != NULL; before_iter = before_iter->next) {
280
281 pcmk__related_action_t *before = before_iter->data;
282
283 char *before_name = NULL;
284 char *after_name = NULL;
285 const char *style = "dashed";
286 bool optional = true;
287
288 if (before->state == pe_link_dumped) {
289 optional = false;
290 style = PCMK__VALUE_BOLD;
291 } else if ((uint32_t) before->type == pcmk__ar_none) {
292 continue;
293 } else if (pcmk_is_set(before->action->flags,
294 pcmk_action_added_to_graph)
295 && pcmk_is_set(action->flags, pcmk_action_added_to_graph)
296 && (uint32_t) before->type != pcmk__ar_if_on_same_node_or_target) {
297 optional = false;
298 }
299
300 if (all_actions || !optional) {
301 before_name = create_action_name(before->action, verbose);
302 after_name = create_action_name(action, verbose);
303 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
304 before_name, after_name, style);
305 free(before_name);
306 free(after_name);
307 }
308 }
309 }
310
311 fprintf(dot_strm, "}\n");
312 fflush(dot_strm);
313 fclose(dot_strm);
314 return pcmk_rc_ok;
315 }
316
317
318
319
320
321
322
323
324
325
326
327
328
329 static void
330 profile_file(const char *xml_file, long long repeat,
331 pcmk_scheduler_t *scheduler, const char *use_date)
332 {
333 pcmk__output_t *out = scheduler->priv;
334 xmlNode *cib_object = NULL;
335 clock_t start = 0;
336 clock_t end;
337 unsigned long long scheduler_flags = pcmk_sched_no_compat;
338
339 pcmk__assert(out != NULL);
340
341 cib_object = pcmk__xml_read(xml_file);
342 start = clock();
343
344 if (pcmk_find_cib_element(cib_object, PCMK_XE_STATUS) == NULL) {
345 pcmk__xe_create(cib_object, PCMK_XE_STATUS);
346 }
347
348 if (pcmk__update_configured_schema(&cib_object, false) != pcmk_rc_ok) {
349 free_xml(cib_object);
350 return;
351 }
352
353 if (!pcmk__validate_xml(cib_object, NULL, NULL, NULL)) {
354 free_xml(cib_object);
355 return;
356 }
357
358 if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
359 scheduler_flags |= pcmk_sched_output_scores;
360 }
361 if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
362 scheduler_flags |= pcmk_sched_show_utilization;
363 }
364
365 for (int i = 0; i < repeat; ++i) {
366 xmlNode *input = cib_object;
367
368 if (repeat > 1) {
369 input = pcmk__xml_copy(NULL, cib_object);
370 }
371 scheduler->input = input;
372 set_effective_date(scheduler, false, use_date);
373 pcmk__schedule_actions(input, scheduler_flags, scheduler);
374 pe_reset_working_set(scheduler);
375 }
376
377 end = clock();
378 out->message(out, "profile", xml_file, start, end);
379 }
380
381 void
382 pcmk__profile_dir(const char *dir, long long repeat,
383 pcmk_scheduler_t *scheduler, const char *use_date)
384 {
385 pcmk__output_t *out = scheduler->priv;
386 struct dirent **namelist;
387
388 int file_num = scandir(dir, &namelist, 0, alphasort);
389
390 pcmk__assert(out != NULL);
391
392 if (file_num > 0) {
393 struct stat prop;
394 char buffer[FILENAME_MAX];
395
396 out->begin_list(out, NULL, NULL, "Timings");
397
398 while (file_num--) {
399 if ('.' == namelist[file_num]->d_name[0]) {
400 free(namelist[file_num]);
401 continue;
402
403 } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
404 ".xml")) {
405 free(namelist[file_num]);
406 continue;
407 }
408 snprintf(buffer, sizeof(buffer), "%s/%s",
409 dir, namelist[file_num]->d_name);
410 if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
411 profile_file(buffer, repeat, scheduler, use_date);
412 }
413 free(namelist[file_num]);
414 }
415 free(namelist);
416
417 out->end_list(out);
418 }
419 }
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434 static void
435 set_effective_date(pcmk_scheduler_t *scheduler, bool print_original,
436 const char *use_date)
437 {
438 pcmk__output_t *out = scheduler->priv;
439 time_t original_date = 0;
440
441 pcmk__assert(out != NULL);
442
443 crm_element_value_epoch(scheduler->input, PCMK_XA_EXECUTION_DATE,
444 &original_date);
445
446 if (use_date) {
447 scheduler->now = crm_time_new(use_date);
448 out->info(out, "Setting effective cluster time: %s", use_date);
449 crm_time_log(LOG_NOTICE, "Pretending 'now' is", scheduler->now,
450 crm_time_log_date | crm_time_log_timeofday);
451
452 } else if (original_date != 0) {
453 scheduler->now = pcmk__copy_timet(original_date);
454
455 if (print_original) {
456 char *when = crm_time_as_string(scheduler->now,
457 crm_time_log_date|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|cib_scope_local) == 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 free_xml(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
604
605 key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource, op->op_type,
606 op->interval_ms, node);
607 if (strncasecmp(key, spec, strlen(key)) == 0) {
608 match_name = resource;
609 }
610 free(key);
611
612
613 if ((match_name == NULL)
614 && (strcmp(resource, resource_config_name) != 0)) {
615
616 key = crm_strdup_printf(PCMK__OP_FMT "@%s=", resource_config_name,
617 op->op_type, op->interval_ms, node);
618 if (strncasecmp(key, spec, strlen(key)) == 0) {
619 match_name = resource_config_name;
620 }
621 free(key);
622 }
623
624 if (match_name == NULL) {
625 continue;
626 }
627
628
629 rc = sscanf(spec, "%*[^=]=%d", (int *) &op->rc);
630 if (rc != 1) {
631 out->err(out, "Invalid failed operation '%s' "
632 "(result code must be integer)", spec);
633 continue;
634 }
635
636 out->info(out, "Pretending action %d failed with rc=%d",
637 action->id, op->rc);
638 pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
639 graph->abort_priority = PCMK_SCORE_INFINITY;
640 pcmk__inject_failcount(out, fake_cib, cib_node, match_name, op->op_type,
641 op->interval_ms, op->rc);
642 break;
643 }
644
645 pcmk__inject_action_result(cib_resource, op, target_outcome);
646 lrmd_free_event(op);
647 rc = fake_cib->cmds->modify(fake_cib, PCMK_XE_STATUS, cib_node,
648 cib_sync_call|cib_scope_local);
649 pcmk__assert(rc == pcmk_ok);
650
651 done:
652 free(node);
653 free_xml(cib_node);
654 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
655 pcmk__update_graph(graph, action);
656 return pcmk_rc_ok;
657 }
658
659
660
661
662
663
664
665
666
667
668 static int
669 simulate_cluster_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
670 {
671 const char *node = crm_element_value(action->xml, PCMK__META_ON_NODE);
672 const char *task = crm_element_value(action->xml, PCMK_XA_OPERATION);
673 xmlNode *rsc = pcmk__xe_first_child(action->xml, PCMK_XE_PRIMITIVE, NULL,
674 NULL);
675
676 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
677 out->message(out, "inject-cluster-action", node, task, rsc);
678 pcmk__update_graph(graph, action);
679 return pcmk_rc_ok;
680 }
681
682
683
684
685
686
687
688
689
690
691 static int
692 simulate_fencing_action(pcmk__graph_t *graph, pcmk__graph_action_t *action)
693 {
694 const char *op = crm_meta_value(action->params, PCMK__META_STONITH_ACTION);
695 char *target = crm_element_value_copy(action->xml, PCMK__META_ON_NODE);
696
697 out->message(out, "inject-fencing-action", target, op);
698
699 if (!pcmk__str_eq(op, PCMK_ACTION_ON, pcmk__str_casei)) {
700 int rc = pcmk_ok;
701 GString *xpath = g_string_sized_new(512);
702
703
704 xmlNode *cib_node = pcmk__inject_node_state_change(fake_cib, target,
705 false);
706
707 pcmk__assert(cib_node != NULL);
708 crm_xml_add(cib_node, PCMK_XA_CRM_DEBUG_ORIGIN, __func__);
709 rc = fake_cib->cmds->replace(fake_cib, PCMK_XE_STATUS, cib_node,
710 cib_sync_call|cib_scope_local);
711 pcmk__assert(rc == pcmk_ok);
712
713
714 pcmk__g_strcat(xpath,
715 "//" PCMK__XE_NODE_STATE
716 "[@" PCMK_XA_UNAME "='", target, "']/" PCMK__XE_LRM,
717 NULL);
718 fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
719 cib_xpath|cib_sync_call|cib_scope_local);
720
721 g_string_truncate(xpath, 0);
722 pcmk__g_strcat(xpath,
723 "//" PCMK__XE_NODE_STATE
724 "[@" PCMK_XA_UNAME "='", target, "']"
725 "/" PCMK__XE_TRANSIENT_ATTRIBUTES, NULL);
726 fake_cib->cmds->remove(fake_cib, (const char *) xpath->str, NULL,
727 cib_xpath|cib_sync_call|cib_scope_local);
728
729 free_xml(cib_node);
730 g_string_free(xpath, TRUE);
731 }
732
733 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
734 pcmk__update_graph(graph, action);
735 free(target);
736 return pcmk_rc_ok;
737 }
738
739 enum pcmk__graph_status
740 pcmk__simulate_transition(pcmk_scheduler_t *scheduler, cib_t *cib,
741 const GList *op_fail_list)
742 {
743 pcmk__graph_t *transition = NULL;
744 enum pcmk__graph_status graph_rc;
745
746 pcmk__graph_functions_t simulation_fns = {
747 simulate_pseudo_action,
748 simulate_resource_action,
749 simulate_cluster_action,
750 simulate_fencing_action,
751 };
752
753 out = scheduler->priv;
754
755 fake_cib = cib;
756 fake_op_fail_list = op_fail_list;
757
758 if (!out->is_quiet(out)) {
759 out->begin_list(out, NULL, NULL, "Executing Cluster Transition");
760 }
761
762 pcmk__set_graph_functions(&simulation_fns);
763 transition = pcmk__unpack_graph(scheduler->graph, crm_system_name);
764 pcmk__log_graph(LOG_DEBUG, transition);
765
766 fake_resource_list = scheduler->resources;
767 do {
768 graph_rc = pcmk__execute_graph(transition);
769 } while (graph_rc == pcmk__graph_active);
770 fake_resource_list = NULL;
771
772 if (graph_rc != pcmk__graph_complete) {
773 out->err(out, "Transition failed: %s",
774 pcmk__graph_status2text(graph_rc));
775 pcmk__log_graph(LOG_ERR, transition);
776 out->err(out, "An invalid transition was produced");
777 }
778 pcmk__free_graph(transition);
779
780 if (!out->is_quiet(out)) {
781
782 xmlNode *cib_object = NULL;
783 int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
784 cib_sync_call|cib_scope_local);
785
786 pcmk__assert(rc == pcmk_ok);
787 pe_reset_working_set(scheduler);
788 scheduler->input = cib_object;
789 out->end_list(out);
790 }
791 return graph_rc;
792 }
793
794 int
795 pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out,
796 const pcmk_injections_t *injections, unsigned int flags,
797 uint32_t section_opts, const char *use_date,
798 const char *input_file, const char *graph_file,
799 const char *dot_file)
800 {
801 int printed = pcmk_rc_no_output;
802 int rc = pcmk_rc_ok;
803 xmlNodePtr input = NULL;
804 cib_t *cib = NULL;
805
806 rc = cib__signon_query(out, &cib, &input);
807 if (rc != pcmk_rc_ok) {
808 goto simulate_done;
809 }
810
811 reset(scheduler, input, out, use_date, flags);
812 cluster_status(scheduler);
813
814 if ((cib->variant == cib_native)
815 && pcmk_is_set(section_opts, pcmk_section_times)) {
816 if (pcmk__our_nodename == NULL) {
817
818 pcmk__query_node_name(out, 0, &pcmk__our_nodename, 0);
819 }
820 scheduler->localhost = pcmk__our_nodename;
821 }
822
823 if (!out->is_quiet(out)) {
824 const bool show_pending = pcmk_is_set(flags, pcmk_sim_show_pending);
825
826 if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
827 printed = out->message(out, "maint-mode", scheduler->flags);
828 }
829
830 if (scheduler->disabled_resources || scheduler->blocked_resources) {
831 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
832 printed = out->info(out,
833 "%d of %d resource instances DISABLED and "
834 "%d BLOCKED from further action due to failure",
835 scheduler->disabled_resources,
836 scheduler->ninstances,
837 scheduler->blocked_resources);
838 }
839
840
841
842
843 print_cluster_status(scheduler, (show_pending? pcmk_show_pending : 0),
844 section_opts, "Current cluster status",
845 (printed == pcmk_rc_ok));
846 printed = pcmk_rc_ok;
847 }
848
849
850 if ((injections->node_down != NULL)
851 || (injections->node_fail != NULL)
852 || (injections->node_up != NULL)
853 || (injections->op_inject != NULL)
854 || (injections->ticket_activate != NULL)
855 || (injections->ticket_grant != NULL)
856 || (injections->ticket_revoke != NULL)
857 || (injections->ticket_standby != NULL)
858 || (injections->watchdog != NULL)) {
859
860 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
861 pcmk__inject_scheduler_input(scheduler, cib, injections);
862 printed = pcmk_rc_ok;
863
864 rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
865 if (rc != pcmk_rc_ok) {
866 rc = pcmk_legacy2rc(rc);
867 goto simulate_done;
868 }
869
870 cleanup_calculations(scheduler);
871 reset(scheduler, input, out, use_date, flags);
872 cluster_status(scheduler);
873 }
874
875 if (input_file != NULL) {
876 rc = pcmk__xml_write_file(input, input_file, false, NULL);
877 if (rc != pcmk_rc_ok) {
878 goto simulate_done;
879 }
880 }
881
882 if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
883 pcmk__output_t *logger_out = NULL;
884 unsigned long long scheduler_flags = pcmk_sched_no_compat;
885
886 if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
887 scheduler_flags |= pcmk_sched_output_scores;
888 }
889 if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
890 scheduler_flags |= pcmk_sched_show_utilization;
891 }
892
893 if (pcmk_all_flags_set(scheduler->flags,
894 pcmk_sched_output_scores
895 |pcmk_sched_show_utilization)) {
896 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
897 out->begin_list(out, NULL, NULL,
898 "Assignment Scores and Utilization Information");
899 printed = pcmk_rc_ok;
900
901 } else if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
902 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
903 out->begin_list(out, NULL, NULL, "Assignment Scores");
904 printed = pcmk_rc_ok;
905
906 } else if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
907 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
908 out->begin_list(out, NULL, NULL, "Utilization Information");
909 printed = pcmk_rc_ok;
910
911 } else {
912 rc = pcmk__log_output_new(&logger_out);
913 if (rc != pcmk_rc_ok) {
914 goto simulate_done;
915 }
916 pe__register_messages(logger_out);
917 pcmk__register_lib_messages(logger_out);
918 scheduler->priv = logger_out;
919 }
920
921 pcmk__schedule_actions(input, scheduler_flags, scheduler);
922
923 if (logger_out == NULL) {
924 out->end_list(out);
925 } else {
926 logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
927 pcmk__output_free(logger_out);
928 scheduler->priv = out;
929 }
930
931 input = NULL;
932
933 if (graph_file != NULL) {
934 rc = pcmk__xml_write_file(scheduler->graph, graph_file, false,
935 NULL);
936 if (rc != pcmk_rc_ok) {
937 rc = pcmk_rc_graph_error;
938 goto simulate_done;
939 }
940 }
941
942 if (dot_file != NULL) {
943 rc = write_sim_dotfile(scheduler, dot_file,
944 pcmk_is_set(flags, pcmk_sim_all_actions),
945 pcmk_is_set(flags, pcmk_sim_verbose));
946 if (rc != pcmk_rc_ok) {
947 rc = pcmk_rc_dot_error;
948 goto simulate_done;
949 }
950 }
951
952 if (!out->is_quiet(out)) {
953 print_transition_summary(scheduler, printed == pcmk_rc_ok);
954 }
955 }
956
957 rc = pcmk_rc_ok;
958
959 if (!pcmk_is_set(flags, pcmk_sim_simulate)) {
960 goto simulate_done;
961 }
962
963 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
964 if (pcmk__simulate_transition(scheduler, cib, injections->op_fail)
965 != pcmk__graph_complete) {
966 rc = pcmk_rc_invalid_transition;
967 }
968
969 if (out->is_quiet(out)) {
970 goto simulate_done;
971 }
972
973 set_effective_date(scheduler, true, use_date);
974
975 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
976 pcmk__set_scheduler_flags(scheduler, pcmk_sched_output_scores);
977 }
978 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
979 pcmk__set_scheduler_flags(scheduler, pcmk_sched_show_utilization);
980 }
981
982 cluster_status(scheduler);
983 print_cluster_status(scheduler, 0, section_opts, "Revised Cluster Status",
984 true);
985
986 simulate_done:
987 cib__clean_up_connection(&cib);
988 return rc;
989 }
990
991 int
992 pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler,
993 const pcmk_injections_t *injections, unsigned int flags,
994 unsigned int section_opts, const char *use_date,
995 const char *input_file, const char *graph_file,
996 const char *dot_file)
997 {
998 pcmk__output_t *out = NULL;
999 int rc = pcmk_rc_ok;
1000
1001 rc = pcmk__xml_output_new(&out, xml);
1002 if (rc != pcmk_rc_ok) {
1003 return rc;
1004 }
1005
1006 pe__register_messages(out);
1007 pcmk__register_lib_messages(out);
1008
1009 rc = pcmk__simulate(scheduler, out, injections, flags, section_opts,
1010 use_date, input_file, graph_file, dot_file);
1011 pcmk__xml_output_finish(out, pcmk_rc2exitc(rc), xml);
1012 return rc;
1013 }