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,
72 XML_LRM_ATTR_INTERVAL_MS, 0,
73 &interval_ms) != pcmk_rc_ok) {
74 interval_ms = 0;
75 }
76
77 if (pcmk__strcase_any_of(action->task, PCMK_ACTION_NOTIFY,
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
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 false, section_opts, show_opts, NULL, 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;
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;
196 set_effective_date(scheduler, true, use_date);
197 if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
198 pe__set_working_set_flags(scheduler, pcmk_sched_sanitized);
199 }
200 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
201 pe__set_working_set_flags(scheduler, pcmk_sched_output_scores);
202 }
203 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
204 pe__set_working_set_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->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 = "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 pe__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->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,
293 pcmk_action_added_to_graph)
294 && pcmk_is_set(action->flags, pcmk_action_added_to_graph)
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
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;
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
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 = (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);
370 pe_reset_working_set(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
417
418
419
420
421
422
423
424
425
426
427
428
429
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,
445 crm_time_log_date | crm_time_log_timeofday);
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,
452 crm_time_log_date|crm_time_log_timeofday);
453
454 out->info(out, "Using the original execution date of: %s", when);
455 free(when);
456 }
457 }
458 }
459
460
461
462
463
464
465
466
467
468
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
475 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
476 out->message(out, "inject-pseudo-action", node, task);
477
478 pcmk__update_graph(graph, action);
479 return pcmk_rc_ok;
480 }
481
482
483
484
485
486
487
488
489
490
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,
505 XML_ATTR_TE_TARGET_RC);
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
511 char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
512 char *uuid = NULL;
513 const char *router_node = crm_element_value(action->xml,
514 XML_LRM_ATTR_ROUTER_NODE);
515
516
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;
520 }
521
522 if (action_rsc == NULL) {
523 crm_log_xml_err(action->xml, "Bad");
524 free(node);
525 return EPROTO;
526 }
527
528
529
530
531
532
533 resource_config_name = crm_element_value(action_rsc, XML_ATTR_ID);
534 if (resource_config_name == NULL) {
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
550 if (pcmk__strcase_any_of(operation, PCMK_ACTION_DELETE,
551 PCMK_ACTION_META_DATA, NULL)) {
552 out->message(out, "inject-rsc-action", resource, operation, node,
553 (guint) 0);
554 goto done;
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,
564 cib_sync_call|cib_scope_local) == pcmk_ok);
565
566
567 uuid = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET_UUID);
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
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
586 op = pcmk__event_from_graph_action(cib_resource, action, PCMK_EXEC_DONE,
587 target_outcome, "User-injected result");
588 out->message(out, "inject-rsc-action", resource, op->op_type, node,
589 op->interval_ms);
590
591
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
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
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;
620 }
621
622
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;
628 }
629
630 out->info(out, "Pretending action %d failed with rc=%d",
631 action->id, op->rc);
632 pcmk__set_graph_action_flags(action, pcmk__graph_action_failed);
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,
642 cib_sync_call|cib_scope_local);
643 CRM_ASSERT(rc == pcmk_ok);
644
645 done:
646 free(node);
647 free_xml(cib_node);
648 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
649 pcmk__update_graph(graph, action);
650 return pcmk_rc_ok;
651 }
652
653
654
655
656
657
658
659
660
661
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
669 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
670 out->message(out, "inject-cluster-action", node, task, rsc);
671 pcmk__update_graph(graph, action);
672 return pcmk_rc_ok;
673 }
674
675
676
677
678
679
680
681
682
683
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");
688 char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET);
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
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,
703 cib_sync_call|cib_scope_local);
704 CRM_ASSERT(rc == pcmk_ok);
705
706
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,
712 cib_xpath|cib_sync_call|cib_scope_local);
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,
720 cib_xpath|cib_sync_call|cib_scope_local);
721
722 free_xml(cib_node);
723 g_string_free(xpath, TRUE);
724 }
725
726 pcmk__set_graph_action_flags(action, pcmk__graph_action_confirmed);
727 pcmk__update_graph(graph, action);
728 free(target);
729 return pcmk_rc_ok;
730 }
731
732 enum pcmk__graph_status
733 pcmk__simulate_transition(pcmk_scheduler_t *scheduler, cib_t *cib,
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);
756 transition = pcmk__unpack_graph(scheduler->graph, crm_system_name);
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
775 xmlNode *cib_object = NULL;
776 int rc = fake_cib->cmds->query(fake_cib, NULL, &cib_object,
777 cib_sync_call|cib_scope_local);
778
779 CRM_ASSERT(rc == pcmk_ok);
780 pe_reset_working_set(scheduler);
781 scheduler->input = cib_object;
782 out->end_list(out);
783 }
784 return graph_rc;
785 }
786
787 int
788 pcmk__simulate(pcmk_scheduler_t *scheduler, pcmk__output_t *out,
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);
805 cluster_status(scheduler);
806
807 if ((cib->variant == cib_native)
808 && pcmk_is_set(section_opts, pcmk_section_times)) {
809 if (pcmk__our_nodename == NULL) {
810
811 pcmk__query_node_name(out, 0, &pcmk__our_nodename, 0);
812 }
813 scheduler->localhost = pcmk__our_nodename;
814 }
815
816 if (!out->is_quiet(out)) {
817 const bool show_pending = pcmk_is_set(flags, pcmk_sim_show_pending);
818
819 if (pcmk_is_set(scheduler->flags, pcmk_sched_in_maintenance)) {
820 printed = out->message(out, "maint-mode", scheduler->flags);
821 }
822
823 if (scheduler->disabled_resources || scheduler->blocked_resources) {
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",
828 scheduler->disabled_resources,
829 scheduler->ninstances,
830 scheduler->blocked_resources);
831 }
832
833
834
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
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
863 cleanup_calculations(scheduler);
864 reset(scheduler, input, out, use_date, flags);
865 cluster_status(scheduler);
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
880 if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
881 scheduler_flags |= pcmk_sched_output_scores;
882 }
883 if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
884 scheduler_flags |= pcmk_sched_show_utilization;
885 }
886
887 if (pcmk_all_flags_set(scheduler->flags,
888 pcmk_sched_output_scores
889 |pcmk_sched_show_utilization)) {
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
895 } else if (pcmk_is_set(scheduler->flags, pcmk_sched_output_scores)) {
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
900 } else if (pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
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;
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,
937 pcmk_is_set(flags, pcmk_sim_all_actions),
938 pcmk_is_set(flags, pcmk_sim_verbose));
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
952 if (!pcmk_is_set(flags, pcmk_sim_simulate)) {
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)
958 != pcmk__graph_complete) {
959 rc = pcmk_rc_invalid_transition;
960 }
961
962 if (out->is_quiet(out)) {
963 goto simulate_done;
964 }
965
966 set_effective_date(scheduler, true, use_date);
967
968 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
969 pe__set_working_set_flags(scheduler, pcmk_sched_output_scores);
970 }
971 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
972 pe__set_working_set_flags(scheduler, pcmk_sched_show_utilization);
973 }
974
975 cluster_status(scheduler);
976 print_cluster_status(scheduler, 0, section_opts, "Revised Cluster Status",
977 true);
978
979 simulate_done:
980 cib__clean_up_connection(&cib);
981 return rc;
982 }
983
984 int
985 pcmk_simulate(xmlNodePtr *xml, pcmk_scheduler_t *scheduler,
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
999 pe__register_messages(out);
1000 pcmk__register_lib_messages(out);
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 }