blob: 4d20af06eef537b86bae5d50649334d4ea070909 [file] [log] [blame]
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <crm/transition.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <crm/msg_xml.h>
#include <crm/cib.h>
#include <glib.h>
#include <pengine.h>
#include <allocate.h>
#if HAVE_LIBXML2
# include <libxml/parser.h>
#endif
gboolean use_stdin = FALSE;
gboolean do_simulation = FALSE;
gboolean inhibit_exit = FALSE;
gboolean all_actions = FALSE;
extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now);
extern void cleanup_calculations(pe_working_set_t * data_set);
char *use_date = NULL;
FILE *dot_strm = NULL;
#define DOT_PREFIX "PE_DOT: "
/* #define DOT_PREFIX "" */
#define dot_write(fmt...) if(dot_strm != NULL) { \
fprintf(dot_strm, fmt); \
fprintf(dot_strm, "\n"); \
} else { \
crm_debug(DOT_PREFIX""fmt); \
}
static void
init_dotfile(void)
{
dot_write(" digraph \"g\" {");
/* dot_write(" size = \"30,30\""); */
/* dot_write(" graph ["); */
/* dot_write(" fontsize = \"12\""); */
/* dot_write(" fontname = \"Times-Roman\""); */
/* dot_write(" fontcolor = \"black\""); */
/* dot_write(" bb = \"0,0,398.922306,478.927856\""); */
/* dot_write(" color = \"black\""); */
/* dot_write(" ]"); */
/* dot_write(" node ["); */
/* dot_write(" fontsize = \"12\""); */
/* dot_write(" fontname = \"Times-Roman\""); */
/* dot_write(" fontcolor = \"black\""); */
/* dot_write(" shape = \"ellipse\""); */
/* dot_write(" color = \"black\""); */
/* dot_write(" ]"); */
/* dot_write(" edge ["); */
/* dot_write(" fontsize = \"12\""); */
/* dot_write(" fontname = \"Times-Roman\""); */
/* dot_write(" fontcolor = \"black\""); */
/* dot_write(" color = \"black\""); */
/* dot_write(" ]"); */
}
static char *
create_action_name(action_t * action)
{
char *action_name = NULL;
const char *action_host = NULL;
if (action->node) {
action_host = action->node->details->uname;
action_name = crm_concat(action->uuid, action_host, ' ');
} else if (is_set(action->flags, pe_action_pseudo)) {
action_name = strdup(action->uuid);
} else {
action_host = "<none>";
action_name = crm_concat(action->uuid, action_host, ' ');
}
if (safe_str_eq(action->task, RSC_CANCEL)) {
char *tmp_action_name = action_name;
action_name = crm_concat("Cancel", tmp_action_name, ' ');
free(tmp_action_name);
}
return action_name;
}
gboolean USE_LIVE_CIB = FALSE;
/* *INDENT-OFF* */
static struct crm_option long_options[] = {
/* Top-level Options */
{"help", 0, 0, '?', "This text"},
{"version", 0, 0, '$', "Version information" },
{"verbose", 0, 0, 'V', "Increase debug output\n"},
{"simulate", 0, 0, 'S', "Simulate the transition's execution to find invalid graphs\n"},
{"show-scores", 0, 0, 's', "Display resource allocation scores"},
{"show-utilization", 0, 0, 'U', "Display utilization information"},
{"all-actions", 0, 0, 'a', "Display all possible actions - even ones not part of the transition graph"},
{"live-check", 0, 0, 'L', "Connect to the CIB and use the current contents as input"},
{"xml-text", 1, 0, 'X', "Retrieve XML from the supplied string"},
{"xml-file", 1, 0, 'x', "Retrieve XML from the named file"},
/* {"xml-pipe", 0, 0, 'p', "Retrieve XML from stdin\n"}, */
{"save-input", 1, 0, 'I', "\tSave the input to the named file"},
{"save-graph", 1, 0, 'G', "\tSave the transition graph (XML format) to the named file"},
{"save-dotfile",1, 0, 'D', "Save the transition graph (DOT format) to the named file\n"},
{0, 0, 0, 0}
};
/* *INDENT-ON* */
int
main(int argc, char **argv)
{
GListPtr lpc = NULL;
gboolean process = TRUE;
gboolean all_good = TRUE;
enum transition_status graph_rc = -1;
crm_graph_t *transition = NULL;
crm_time_t *a_date = NULL;
cib_t *cib_conn = NULL;
xmlNode *cib_object = NULL;
int argerr = 0;
int flag;
char *msg_buffer = NULL;
gboolean optional = FALSE;
pe_working_set_t data_set;
const char *source = NULL;
const char *xml_file = NULL;
const char *dot_file = NULL;
const char *graph_file = NULL;
const char *input_file = NULL;
const char *input_xml = NULL;
/* disable glib's fancy allocators that can't be free'd */
GMemVTable vtable;
vtable.malloc = malloc;
vtable.realloc = realloc;
vtable.free = free;
vtable.calloc = calloc;
vtable.try_malloc = malloc;
vtable.try_realloc = realloc;
g_mem_set_vtable(&vtable);
crm_log_cli_init("ptest");
crm_set_options(NULL, "[-?Vv] -[Xxp] {other options}", long_options,
"Calculate the cluster's response to the supplied cluster state\n"
"\nSuperseded by crm_simulate and likely to be removed in a future release\n\n");
while (1) {
int option_index = 0;
flag = crm_get_option(argc, argv, &option_index);
if (flag == -1)
break;
switch (flag) {
case 'S':
do_simulation = TRUE;
break;
case 'a':
all_actions = TRUE;
break;
case 'w':
inhibit_exit = TRUE;
break;
case 'X':
/*use_stdin = TRUE; */
input_xml = optarg;
break;
case 's':
show_scores = TRUE;
break;
case 'U':
show_utilization = TRUE;
break;
case 'x':
xml_file = optarg;
break;
case 'd':
use_date = optarg;
break;
case 'D':
dot_file = optarg;
break;
case 'G':
graph_file = optarg;
break;
case 'I':
input_file = optarg;
break;
case 'V':
crm_bump_log_level(argc, argv);
break;
case 'L':
USE_LIVE_CIB = TRUE;
break;
case '$':
case '?':
crm_help(flag, 0);
break;
default:
fprintf(stderr, "Option -%c is not yet supported\n", flag);
++argerr;
break;
}
}
if (optind < argc) {
printf("non-option ARGV-elements: ");
while (optind < argc) {
printf("%s ", argv[optind++]);
}
printf("\n");
}
if (optind > argc) {
++argerr;
}
if (argerr) {
crm_err("%d errors in option parsing", argerr);
crm_help('?', 1);
}
if (USE_LIVE_CIB) {
int rc = pcmk_ok;
source = "live cib";
cib_conn = cib_new();
rc = cib_conn->cmds->signon(cib_conn, "ptest", cib_command);
if (rc == pcmk_ok) {
crm_info("Reading XML from: live cluster");
cib_object = get_cib_copy(cib_conn);
} else {
fprintf(stderr, "Live CIB query failed: %s\n", pcmk_strerror(rc));
return 3;
}
if (cib_object == NULL) {
fprintf(stderr, "Live CIB query failed: empty result\n");
return 3;
}
} else if (xml_file != NULL) {
source = xml_file;
cib_object = filename2xml(xml_file);
} else if (use_stdin) {
source = "stdin";
cib_object = filename2xml(NULL);
} else if (input_xml) {
source = "input string";
cib_object = string2xml(input_xml);
}
if (cib_object == NULL && source) {
fprintf(stderr, "Could not parse configuration input from: %s\n", source);
return 4;
} else if (cib_object == NULL) {
fprintf(stderr, "No configuration specified\n");
crm_help('?', 1);
}
if (get_object_root(XML_CIB_TAG_STATUS, cib_object) == NULL) {
create_xml_node(cib_object, XML_CIB_TAG_STATUS);
}
if (cli_config_update(&cib_object, NULL, FALSE) == FALSE) {
free_xml(cib_object);
return -ENOKEY;
}
if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
free_xml(cib_object);
return -pcmk_err_schema_validation;
}
if (input_file != NULL) {
FILE *input_strm = fopen(input_file, "w");
if (input_strm == NULL) {
crm_perror(LOG_ERR, "Could not open %s for writing", input_file);
} else {
msg_buffer = dump_xml_formatted(cib_object);
if (fprintf(input_strm, "%s\n", msg_buffer) < 0) {
crm_perror(LOG_ERR, "Write to %s failed", input_file);
}
fflush(input_strm);
fclose(input_strm);
free(msg_buffer);
}
}
if (use_date != NULL) {
a_date = crm_time_new(use_date);
crm_time_log(LOG_WARNING, "Set fake 'now' to", a_date,
crm_time_log_date | crm_time_log_timeofday);
crm_time_log(LOG_WARNING, "Set fake 'now' to (localtime)", a_date,
crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone);
}
set_working_set_defaults(&data_set);
if (process) {
if (show_scores && show_utilization) {
fprintf(stdout, "Allocation scores and utilization information:\n");
} else if (show_scores) {
fprintf(stdout, "Allocation scores:\n");
} else if (show_utilization) {
fprintf(stdout, "Utilization information:\n");
}
do_calculations(&data_set, cib_object, a_date);
}
msg_buffer = dump_xml_formatted(data_set.graph);
if (safe_str_eq(graph_file, "-")) {
fprintf(stdout, "%s\n", msg_buffer);
fflush(stdout);
} else if (graph_file != NULL) {
FILE *graph_strm = fopen(graph_file, "w");
if (graph_strm == NULL) {
crm_perror(LOG_ERR, "Could not open %s for writing", graph_file);
} else {
if (fprintf(graph_strm, "%s\n\n", msg_buffer) < 0) {
crm_perror(LOG_ERR, "Write to %s failed", graph_file);
}
fflush(graph_strm);
fclose(graph_strm);
}
}
free(msg_buffer);
if (dot_file != NULL) {
dot_strm = fopen(dot_file, "w");
if (dot_strm == NULL) {
crm_perror(LOG_ERR, "Could not open %s for writing", dot_file);
}
}
if (dot_strm == NULL) {
goto simulate;
}
init_dotfile();
for (lpc = data_set.actions; lpc != NULL; lpc = lpc->next) {
action_t *action = (action_t *) lpc->data;
const char *style = "filled";
const char *font = "black";
const char *color = "black";
const char *fill = NULL;
char *action_name = create_action_name(action);
crm_trace("Action %d: %p", action->id, action);
if (is_set(action->flags, pe_action_pseudo)) {
font = "orange";
}
style = "dashed";
if (is_set(action->flags, pe_action_dumped)) {
style = "bold";
color = "green";
} else if (action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) {
color = "purple";
if (all_actions == FALSE) {
goto dont_write;
}
} else if (is_set(action->flags, pe_action_optional)) {
color = "blue";
if (all_actions == FALSE) {
goto dont_write;
}
} else {
color = "red";
CRM_CHECK(is_set(action->flags, pe_action_runnable) == FALSE,;
);
}
set_bit(action->flags, pe_action_dumped);
dot_write("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]",
action_name, style, color, font, fill ? "fillcolor=" : "", fill ? fill : "");
dont_write:
free(action_name);
}
for (lpc = data_set.actions; lpc != NULL; lpc = lpc->next) {
action_t *action = (action_t *) lpc->data;
GListPtr lpc2 = NULL;
for (lpc2 = action->actions_before; lpc2 != NULL; lpc2 = lpc2->next) {
action_wrapper_t *before = (action_wrapper_t *) lpc2->data;
char *before_name = NULL;
char *after_name = NULL;
const char *style = "dashed";
optional = TRUE;
if (before->state == pe_link_dumped) {
optional = FALSE;
style = "bold";
} else if (is_set(action->flags, pe_action_pseudo)
&& (before->type & pe_order_stonith_stop)) {
continue;
} else if (before->state == pe_link_dup) {
continue;
} else if (before->type == pe_order_none) {
continue;
} else if (is_set(before->action->flags, pe_action_dumped)
&& is_set(action->flags, pe_action_dumped)) {
optional = FALSE;
}
if (all_actions || optional == FALSE) {
before_name = create_action_name(before->action);
after_name = create_action_name(action);
dot_write("\"%s\" -> \"%s\" [ style = %s]", before_name, after_name, style);
free(before_name);
free(after_name);
}
}
}
dot_write("}");
if (dot_strm != NULL) {
fflush(dot_strm);
fclose(dot_strm);
}
simulate:
if (do_simulation == FALSE) {
goto cleanup;
}
transition = unpack_graph(data_set.graph, "ptest");
print_graph(LOG_DEBUG, transition);
do {
graph_rc = run_graph(transition);
} while (graph_rc == transition_active);
if (graph_rc != transition_complete) {
crm_crit("Transition failed: %s", transition_status(graph_rc));
print_graph(LOG_ERR, transition);
}
destroy_graph(transition);
CRM_CHECK(graph_rc == transition_complete, all_good = FALSE;
crm_err("An invalid transition was produced"));
cleanup:
cleanup_alloc_calculations(&data_set);
crm_log_deinit();
/* required for MallocDebug.app */
if (inhibit_exit) {
GMainLoop *mainloop = g_main_new(FALSE);
g_main_run(mainloop);
}
if (all_good) {
return 0;
}
return graph_rc;
}