This source file includes following definitions.
- create_action_name
- print_cluster_status
- print_transition_summary
- reset
- pcmk__write_sim_dotfile
- pcmk__profile_file
- pcmk__profile_dir
- pcmk__set_effective_date
- 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/pengine/pe_types.h>
15 #include <pacemaker-internal.h>
16 #include <pacemaker.h>
17
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
21
22 static char *
23 create_action_name(pe_action_t *action, bool verbose)
24 {
25 char *action_name = NULL;
26 const char *prefix = "";
27 const char *action_host = NULL;
28 const char *clone_name = NULL;
29 const char *task = action->task;
30
31 if (action->node) {
32 action_host = action->node->details->uname;
33 } else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
34 action_host = "<none>";
35 }
36
37 if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_casei)) {
38 prefix = "Cancel ";
39 task = action->cancel_task;
40 }
41
42 if (action->rsc && action->rsc->clone_name) {
43 clone_name = action->rsc->clone_name;
44 }
45
46 if (clone_name) {
47 char *key = NULL;
48 guint interval_ms = 0;
49
50 if (pcmk__guint_from_hash(action->meta,
51 XML_LRM_ATTR_INTERVAL_MS, 0,
52 &interval_ms) != pcmk_rc_ok) {
53 interval_ms = 0;
54 }
55
56 if (pcmk__strcase_any_of(action->task, RSC_NOTIFY, RSC_NOTIFIED, NULL)) {
57 const char *n_type = g_hash_table_lookup(action->meta, "notify_key_type");
58 const char *n_task = g_hash_table_lookup(action->meta, "notify_key_operation");
59
60 CRM_ASSERT(n_type != NULL);
61 CRM_ASSERT(n_task != NULL);
62 key = pcmk__notify_key(clone_name, n_type, n_task);
63
64 } else {
65 key = pcmk__op_key(clone_name, task, interval_ms);
66 }
67
68 if (action_host) {
69 action_name = crm_strdup_printf("%s%s %s", prefix, key, action_host);
70 } else {
71 action_name = crm_strdup_printf("%s%s", prefix, key);
72 }
73 free(key);
74
75 } else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
76 const char *op = g_hash_table_lookup(action->meta, "stonith_action");
77
78 action_name = crm_strdup_printf("%s%s '%s' %s", prefix, action->task, op, action_host);
79
80 } else if (action->rsc && action_host) {
81 action_name = crm_strdup_printf("%s%s %s", prefix, action->uuid, action_host);
82
83 } else if (action_host) {
84 action_name = crm_strdup_printf("%s%s %s", prefix, action->task, action_host);
85
86 } else {
87 action_name = crm_strdup_printf("%s", action->uuid);
88 }
89
90 if (verbose) {
91 char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
92
93 free(action_name);
94 action_name = with_id;
95 }
96 return action_name;
97 }
98
99 static void
100 print_cluster_status(pe_working_set_t * data_set, unsigned int show_opts,
101 unsigned int section_opts, const char *title, bool print_spacer)
102 {
103 pcmk__output_t *out = data_set->priv;
104 GList *all = NULL;
105
106 section_opts |= pcmk_section_nodes | pcmk_section_resources;
107
108 all = g_list_prepend(all, (gpointer) "*");
109
110 PCMK__OUTPUT_SPACER_IF(out, print_spacer);
111 out->begin_list(out, NULL, NULL, "%s", title);
112 out->message(out, "cluster-status", data_set, 0, NULL, FALSE,
113 section_opts,
114 show_opts | pcmk_show_inactive_rscs | pcmk_show_failed_detail,
115 NULL, all, all);
116 out->end_list(out);
117
118 g_list_free(all);
119 }
120
121 static void
122 print_transition_summary(pe_working_set_t *data_set, bool print_spacer)
123 {
124 pcmk__output_t *out = data_set->priv;
125
126 PCMK__OUTPUT_SPACER_IF(out, print_spacer);
127 out->begin_list(out, NULL, NULL, "Transition Summary");
128 LogNodeActions(data_set);
129 g_list_foreach(data_set->resources, (GFunc) LogActions, data_set);
130 out->end_list(out);
131 }
132
133 static void
134 reset(pe_working_set_t *data_set, xmlNodePtr input, pcmk__output_t *out,
135 char *use_date, unsigned int flags)
136 {
137 data_set->input = input;
138 data_set->priv = out;
139 pcmk__set_effective_date(data_set, true, use_date);
140 if (pcmk_is_set(flags, pcmk_sim_sanitized)) {
141 pe__set_working_set_flags(data_set, pe_flag_sanitized);
142 }
143 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
144 pe__set_working_set_flags(data_set, pe_flag_show_scores);
145 }
146 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
147 pe__set_working_set_flags(data_set, pe_flag_show_utilization);
148 }
149 }
150
151 int
152 pcmk__write_sim_dotfile(pe_working_set_t *data_set, const char *dot_file,
153 bool all_actions, bool verbose)
154 {
155 GList *gIter = NULL;
156 FILE *dot_strm = fopen(dot_file, "w");
157
158 if (dot_strm == NULL) {
159 return errno;
160 }
161
162 fprintf(dot_strm, " digraph \"g\" {\n");
163 for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
164 pe_action_t *action = (pe_action_t *) gIter->data;
165 const char *style = "dashed";
166 const char *font = "black";
167 const char *color = "black";
168 char *action_name = create_action_name(action, verbose);
169
170 if (pcmk_is_set(action->flags, pe_action_pseudo)) {
171 font = "orange";
172 }
173
174 if (pcmk_is_set(action->flags, pe_action_dumped)) {
175 style = "bold";
176 color = "green";
177
178 } else if ((action->rsc != NULL)
179 && !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
180 color = "red";
181 font = "purple";
182 if (!all_actions) {
183 goto do_not_write;
184 }
185
186 } else if (pcmk_is_set(action->flags, pe_action_optional)) {
187 color = "blue";
188 if (!all_actions) {
189 goto do_not_write;
190 }
191
192 } else {
193 color = "red";
194 CRM_CHECK(!pcmk_is_set(action->flags, pe_action_runnable), ;);
195 }
196
197 pe__set_action_flags(action, pe_action_dumped);
198 fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
199 action_name, style, color, font);
200 do_not_write:
201 free(action_name);
202 }
203
204 for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
205 pe_action_t *action = (pe_action_t *) gIter->data;
206
207 GList *gIter2 = NULL;
208
209 for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
210 pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
211
212 char *before_name = NULL;
213 char *after_name = NULL;
214 const char *style = "dashed";
215 bool optional = true;
216
217 if (before->state == pe_link_dumped) {
218 optional = false;
219 style = "bold";
220 } else if (pcmk_is_set(action->flags, pe_action_pseudo)
221 && (before->type & pe_order_stonith_stop)) {
222 continue;
223 } else if (before->type == pe_order_none) {
224 continue;
225 } else if (pcmk_is_set(before->action->flags, pe_action_dumped)
226 && pcmk_is_set(action->flags, pe_action_dumped)
227 && before->type != pe_order_load) {
228 optional = false;
229 }
230
231 if (all_actions || !optional) {
232 before_name = create_action_name(before->action, verbose);
233 after_name = create_action_name(action, verbose);
234 fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
235 before_name, after_name, style);
236 free(before_name);
237 free(after_name);
238 }
239 }
240 }
241
242 fprintf(dot_strm, "}\n");
243 fflush(dot_strm);
244 fclose(dot_strm);
245 return pcmk_rc_ok;
246 }
247
248 void
249 pcmk__profile_file(const char *xml_file, long long repeat, pe_working_set_t *data_set, char *use_date)
250 {
251 pcmk__output_t *out = data_set->priv;
252 xmlNode *cib_object = NULL;
253 clock_t start = 0;
254 clock_t end;
255
256 CRM_ASSERT(out != NULL);
257
258 cib_object = filename2xml(xml_file);
259 start = clock();
260
261 if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
262 create_xml_node(cib_object, XML_CIB_TAG_STATUS);
263 }
264
265 if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
266 free_xml(cib_object);
267 return;
268 }
269
270 if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
271 free_xml(cib_object);
272 return;
273 }
274
275 for (int i = 0; i < repeat; ++i) {
276 xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
277
278 data_set->input = input;
279 pcmk__set_effective_date(data_set, false, use_date);
280 pcmk__schedule_actions(data_set, input, NULL);
281 pe_reset_working_set(data_set);
282 }
283
284 end = clock();
285 out->message(out, "profile", xml_file, start, end);
286 }
287
288 void
289 pcmk__profile_dir(const char *dir, long long repeat, pe_working_set_t *data_set, char *use_date)
290 {
291 pcmk__output_t *out = data_set->priv;
292 struct dirent **namelist;
293
294 int file_num = scandir(dir, &namelist, 0, alphasort);
295
296 CRM_ASSERT(out != NULL);
297
298 if (file_num > 0) {
299 struct stat prop;
300 char buffer[FILENAME_MAX];
301
302 out->begin_list(out, NULL, NULL, "Timings");
303
304 while (file_num--) {
305 if ('.' == namelist[file_num]->d_name[0]) {
306 free(namelist[file_num]);
307 continue;
308
309 } else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
310 ".xml")) {
311 free(namelist[file_num]);
312 continue;
313 }
314 snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
315 if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
316 pcmk__profile_file(buffer, repeat, data_set, use_date);
317 }
318 free(namelist[file_num]);
319 }
320 free(namelist);
321
322 out->end_list(out);
323 }
324 }
325
326 void
327 pcmk__set_effective_date(pe_working_set_t *data_set, bool print_original, char *use_date)
328 {
329 pcmk__output_t *out = data_set->priv;
330 time_t original_date = 0;
331
332 CRM_ASSERT(out != NULL);
333
334 crm_element_value_epoch(data_set->input, "execution-date", &original_date);
335
336 if (use_date) {
337 data_set->now = crm_time_new(use_date);
338 out->info(out, "Setting effective cluster time: %s", use_date);
339 crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
340 crm_time_log_date | crm_time_log_timeofday);
341
342 } else if (original_date) {
343
344 data_set->now = crm_time_new(NULL);
345 crm_time_set_timet(data_set->now, &original_date);
346
347 if (print_original) {
348 char *when = crm_time_as_string(data_set->now,
349 crm_time_log_date|crm_time_log_timeofday);
350
351 out->info(out, "Using the original execution date of: %s", when);
352 free(when);
353 }
354 }
355 }
356
357 int
358 pcmk__simulate(pe_working_set_t *data_set, pcmk__output_t *out, pcmk_injections_t *injections,
359 unsigned int flags, unsigned int section_opts, char *use_date,
360 char *input_file, char *graph_file, char *dot_file)
361 {
362 int printed = pcmk_rc_no_output;
363 int rc = pcmk_rc_ok;
364 xmlNodePtr input = NULL;
365 cib_t *cib = NULL;
366 bool modified = false;
367
368 rc = cib__signon_query(&cib, &input);
369 if (rc != pcmk_rc_ok) {
370 goto simulate_done;
371 }
372
373 reset(data_set, input, out, use_date, flags);
374 cluster_status(data_set);
375
376 if (!out->is_quiet(out)) {
377 if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
378 printed = out->message(out, "maint-mode", data_set->flags);
379 }
380
381 if (data_set->disabled_resources || data_set->blocked_resources) {
382 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
383 printed = out->info(out, "%d of %d resource instances DISABLED and %d BLOCKED "
384 "from further action due to failure",
385 data_set->disabled_resources, data_set->ninstances,
386 data_set->blocked_resources);
387 }
388
389
390
391
392 print_cluster_status(data_set, pcmk_is_set(flags, pcmk_sim_show_pending) ? pcmk_show_pending : 0,
393 section_opts, "Current cluster status", printed == pcmk_rc_ok);
394 printed = pcmk_rc_ok;
395 }
396
397 modified = injections->node_down != NULL || injections->node_fail != NULL ||
398 injections->node_up != NULL || injections->op_inject != NULL ||
399 injections->ticket_activate != NULL || injections->ticket_grant != NULL ||
400 injections->ticket_revoke != NULL || injections->ticket_standby != NULL ||
401 injections->watchdog != NULL || injections->watchdog != NULL;
402
403 if (modified) {
404 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
405 modify_configuration(data_set, cib, injections);
406 printed = pcmk_rc_ok;
407
408 rc = cib->cmds->query(cib, NULL, &input, cib_sync_call);
409 if (rc != pcmk_rc_ok) {
410 rc = pcmk_legacy2rc(rc);
411 goto simulate_done;
412 }
413
414 cleanup_calculations(data_set);
415 reset(data_set, input, out, use_date, flags);
416 cluster_status(data_set);
417 }
418
419 if (input_file != NULL) {
420 rc = write_xml_file(input, input_file, FALSE);
421 if (rc < 0) {
422 rc = pcmk_legacy2rc(rc);
423 goto simulate_done;
424 }
425 }
426
427 if (pcmk_any_flags_set(flags, pcmk_sim_process | pcmk_sim_simulate)) {
428 crm_time_t *local_date = NULL;
429 pcmk__output_t *logger_out = NULL;
430
431 if (pcmk_all_flags_set(data_set->flags, pe_flag_show_scores|pe_flag_show_utilization)) {
432 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
433 out->begin_list(out, NULL, NULL, "Allocation Scores and Utilization Information");
434 printed = pcmk_rc_ok;
435 } else if (pcmk_is_set(data_set->flags, pe_flag_show_scores)) {
436 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
437 out->begin_list(out, NULL, NULL, "Allocation Scores");
438 printed = pcmk_rc_ok;
439 } else if (pcmk_is_set(data_set->flags, pe_flag_show_utilization)) {
440 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
441 out->begin_list(out, NULL, NULL, "Utilization Information");
442 printed = pcmk_rc_ok;
443 } else {
444 logger_out = pcmk__new_logger();
445 if (logger_out == NULL) {
446 rc = pcmk_rc_error;
447 goto simulate_done;
448 }
449
450 data_set->priv = logger_out;
451 }
452
453 pcmk__schedule_actions(data_set, input, local_date);
454
455 if (logger_out == NULL) {
456 out->end_list(out);
457 } else {
458 logger_out->finish(logger_out, CRM_EX_OK, true, NULL);
459 pcmk__output_free(logger_out);
460 data_set->priv = out;
461 }
462
463 input = NULL;
464
465 if (graph_file != NULL) {
466 rc = write_xml_file(data_set->graph, graph_file, FALSE);
467 if (rc < 0) {
468 rc = pcmk_rc_graph_error;
469 goto simulate_done;
470 }
471 }
472
473 if (dot_file != NULL) {
474 rc = pcmk__write_sim_dotfile(data_set, dot_file,
475 pcmk_is_set(flags, pcmk_sim_all_actions),
476 pcmk_is_set(flags, pcmk_sim_verbose));
477 if (rc != pcmk_rc_ok) {
478 rc = pcmk_rc_dot_error;
479 goto simulate_done;
480 }
481 }
482
483 if (!out->is_quiet(out)) {
484 print_transition_summary(data_set, printed == pcmk_rc_ok);
485 }
486 }
487
488 rc = pcmk_rc_ok;
489
490 if (pcmk_is_set(flags, pcmk_sim_simulate)) {
491 PCMK__OUTPUT_SPACER_IF(out, printed == pcmk_rc_ok);
492 if (run_simulation(data_set, cib, injections->op_fail) != transition_complete) {
493 rc = pcmk_rc_invalid_transition;
494 }
495
496 if (!out->is_quiet(out)) {
497 pcmk__set_effective_date(data_set, true, use_date);
498
499 if (pcmk_is_set(flags, pcmk_sim_show_scores)) {
500 pe__set_working_set_flags(data_set, pe_flag_show_scores);
501 }
502 if (pcmk_is_set(flags, pcmk_sim_show_utilization)) {
503 pe__set_working_set_flags(data_set, pe_flag_show_utilization);
504 }
505
506 cluster_status(data_set);
507 print_cluster_status(data_set, 0, section_opts, "Revised Cluster Status", true);
508 }
509 }
510
511 simulate_done:
512 if (cib) {
513 cib->cmds->signoff(cib);
514 cib_delete(cib);
515 }
516
517 return rc;
518 }
519
520 int
521 pcmk_simulate(xmlNodePtr *xml, pe_working_set_t *data_set, pcmk_injections_t *injections,
522 unsigned int flags, unsigned int section_opts, char *use_date,
523 char *input_file, char *graph_file, char *dot_file)
524 {
525 pcmk__output_t *out = NULL;
526 int rc = pcmk_rc_ok;
527
528 rc = pcmk__out_prologue(&out, xml);
529 if (rc != pcmk_rc_ok) {
530 return rc;
531 }
532
533 pe__register_messages(out);
534 pcmk__register_lib_messages(out);
535
536 rc = pcmk__simulate(data_set, out, injections, flags, section_opts,
537 use_date, input_file, graph_file, dot_file);
538 pcmk__out_epilogue(out, xml, rc);
539 return rc;
540 }