Page MenuHomeClusterLabs Projects

crm_simulate.c
No OneTemporary

crm_simulate.c

/*
* Copyright 2009-2020 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/types.h>
#include <dirent.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/common/cmdline_internal.h>
#include <crm/common/util.h>
#include <crm/common/iso8601.h>
#include <crm/pengine/status.h>
#include <pacemaker-internal.h>
#define SUMMARY "crm_simulate - simulate a Pacemaker cluster's response to events"
/* show_scores and show_utilization can't be added to this struct. They
* actually come from include/pcmki/pcmki_scheduler.h where they are
* defined as extern.
*/
struct {
gboolean all_actions;
char *dot_file;
char *graph_file;
gchar *input_file;
guint modified;
GListPtr node_up;
GListPtr node_down;
GListPtr node_fail;
GListPtr op_fail;
GListPtr op_inject;
gchar *output_file;
gboolean print_pending;
gboolean process;
char *quorum;
long long repeat;
gboolean simulate;
gboolean store;
gchar *test_dir;
GListPtr ticket_grant;
GListPtr ticket_revoke;
GListPtr ticket_standby;
GListPtr ticket_activate;
char *use_date;
char *watchdog;
char *xml_file;
} options = {
.print_pending = TRUE,
.repeat = 1
};
cib_t *global_cib = NULL;
bool action_numbers = FALSE;
gboolean quiet = FALSE;
char *temp_shadow = NULL;
extern gboolean bringing_nodes_online;
#define quiet_log(fmt, args...) do { \
if(quiet == FALSE) { \
printf(fmt , ##args); \
} \
} while(0)
#define INDENT " "
static gboolean
in_place_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.store = TRUE;
options.process = TRUE;
options.simulate = TRUE;
return TRUE;
}
static gboolean
live_check_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (options.xml_file) {
free(options.xml_file);
}
options.xml_file = NULL;
return TRUE;
}
static gboolean
node_down_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
options.node_down = g_list_append(options.node_down, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
node_fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
options.node_fail = g_list_append(options.node_fail, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
node_up_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
bringing_nodes_online = TRUE;
options.node_up = g_list_append(options.node_up, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
op_fail_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.process = TRUE;
options.simulate = TRUE;
options.op_fail = g_list_append(options.op_fail, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
op_inject_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
options.op_inject = g_list_append(options.op_inject, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
quorum_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (options.quorum) {
free(options.quorum);
}
options.modified++;
options.quorum = strdup(optarg);
return TRUE;
}
static gboolean
save_dotfile_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (options.dot_file) {
free(options.dot_file);
}
options.process = TRUE;
options.dot_file = strdup(optarg);
return TRUE;
}
static gboolean
save_graph_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (options.graph_file) {
free(options.graph_file);
}
options.process = TRUE;
options.graph_file = strdup(optarg);
return TRUE;
}
static gboolean
show_scores_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.process = TRUE;
show_scores = TRUE;
return TRUE;
}
static gboolean
simulate_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.process = TRUE;
options.simulate = TRUE;
return TRUE;
}
static gboolean
ticket_activate_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
options.ticket_activate = g_list_append(options.ticket_activate, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
ticket_grant_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
options.ticket_grant = g_list_append(options.ticket_grant, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
ticket_revoke_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
options.ticket_revoke = g_list_append(options.ticket_revoke, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
ticket_standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.modified++;
options.ticket_standby = g_list_append(options.ticket_standby, (gchar *) g_strdup(optarg));
return TRUE;
}
static gboolean
utilization_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
options.process = TRUE;
show_utilization = TRUE;
return TRUE;
}
static gboolean
watchdog_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (options.watchdog) {
free(options.watchdog);
}
options.modified++;
options.watchdog = strdup(optarg);
return TRUE;
}
static gboolean
xml_pipe_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **error) {
if (options.xml_file) {
free(options.xml_file);
}
options.xml_file = strdup("-");
return TRUE;
}
static GOptionEntry operation_entries[] = {
{ "run", 'R', 0, G_OPTION_ARG_NONE, &options.process,
"Determine cluster's response to the given configuration and status",
NULL },
{ "simulate", 'S', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, simulate_cb,
"Simulate transition's execution and display resulting cluster status",
NULL },
{ "in-place", 'X', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, in_place_cb,
"Simulate transition's execution and store result back to input file",
NULL },
{ "show-scores", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, show_scores_cb,
"Show allocation scores",
NULL },
{ "show-utilization", 'U', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, utilization_cb,
"Show utilization information",
NULL },
{ "profile", 'P', 0, G_OPTION_ARG_FILENAME, &options.test_dir,
"Run all tests in the named directory to create profiling data",
NULL },
{ "repeat", 'N', 0, G_OPTION_ARG_INT, &options.repeat,
"With --profile, repeat each test N times and print timings",
"N" },
{ "pending", 'j', 0, G_OPTION_ARG_NONE, &options.print_pending,
"Display pending state if 'record-pending' is enabled",
NULL },
{ NULL }
};
static GOptionEntry synthetic_entries[] = {
{ "node-up", 'u', 0, G_OPTION_ARG_CALLBACK, node_up_cb,
"Bring a node online",
"NODE" },
{ "node-down", 'd', 0, G_OPTION_ARG_CALLBACK, node_down_cb,
"Take a node offline",
"NODE" },
{ "node-fail", 'f', 0, G_OPTION_ARG_CALLBACK, node_fail_cb,
"Mark a node as failed",
"NODE" },
{ "op-inject", 'i', 0, G_OPTION_ARG_CALLBACK, op_inject_cb,
"Generate a failure for the cluster to react to in the simulation.\n"
INDENT "See `Operation Specification` help for more information.",
"OPSPEC" },
{ "op-fail", 'F', 0, G_OPTION_ARG_CALLBACK, op_fail_cb,
"If the specified task occurs during the simulation, have it fail with return code ${rc}.\n"
INDENT "The transition will normally stop at the failed action.\n"
INDENT "Save the result with --save-output and re-run with --xml-file.\n"
INDENT "See `Operation Specification` help for more information.",
"OPSPEC" },
{ "set-datetime", 't', 0, G_OPTION_ARG_STRING, &options.use_date,
"Set date/time (ISO 8601 format, see https://en.wikipedia.org/wiki/ISO_8601)",
"DATETIME" },
{ "quorum", 'q', 0, G_OPTION_ARG_CALLBACK, quorum_cb,
"Specify a value for quorum",
"QUORUM" },
{ "watchdog", 'w', 0, G_OPTION_ARG_CALLBACK, watchdog_cb,
"Assume a watchdog device is active",
"DEVICE" },
{ "ticket-grant", 'g', 0, G_OPTION_ARG_CALLBACK, ticket_grant_cb,
"Grant a ticket",
"TICKET" },
{ "ticket-revoke", 'r', 0, G_OPTION_ARG_CALLBACK, ticket_revoke_cb,
"Revoke a ticket",
"TICKET" },
{ "ticket-standby", 'b', 0, G_OPTION_ARG_CALLBACK, ticket_standby_cb,
"Make a ticket standby",
"TICKET" },
{ "ticket-activate", 'e', 0, G_OPTION_ARG_CALLBACK, ticket_activate_cb,
"Activate a ticket",
"TICKET" },
{ NULL }
};
static GOptionEntry output_entries[] = {
{ "save-input", 'I', 0, G_OPTION_ARG_FILENAME, &options.input_file,
"Save the input configuration to the named file",
"FILE" },
{ "save-output", 'O', 0, G_OPTION_ARG_FILENAME, &options.output_file,
"Save the output configuration to the named file",
"FILE" },
{ "save-graph", 'G', 0, G_OPTION_ARG_CALLBACK, save_graph_cb,
"Save the transition graph (XML format) to the named file",
"FILE" },
{ "save-dotfile", 'D', 0, G_OPTION_ARG_CALLBACK, save_dotfile_cb,
"Save the transition graph (DOT format) to the named file",
"FILE" },
{ "all-actions", 'a', 0, G_OPTION_ARG_NONE, &options.all_actions,
"Display all possible actions in DOT graph (even if not part of transition)",
NULL },
{ NULL }
};
static GOptionEntry source_entries[] = {
{ "live-check", 'L', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, live_check_cb,
"Connect to CIB mamager and use the current CIB contents as input",
NULL },
{ "xml-file", 'x', 0, G_OPTION_ARG_FILENAME, &options.xml_file,
"Retrieve XML from the named file",
"FILE" },
{ "xml-pipe", 'p', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, xml_pipe_cb,
"Retrieve XML from stdin",
NULL },
{ NULL }
};
static void
get_date(pe_working_set_t *data_set, bool print_original, char *use_date)
{
time_t original_date = 0;
crm_element_value_epoch(data_set->input, "execution-date", &original_date);
if (use_date) {
data_set->now = crm_time_new(use_date);
quiet_log(" + Setting effective cluster time: %s", use_date);
crm_time_log(LOG_NOTICE, "Pretending 'now' is", data_set->now,
crm_time_log_date | crm_time_log_timeofday);
} else if (original_date) {
data_set->now = crm_time_new(NULL);
crm_time_set_timet(data_set->now, &original_date);
if (print_original) {
char *when = crm_time_as_string(data_set->now,
crm_time_log_date|crm_time_log_timeofday);
printf("Using the original execution date of: %s\n", when);
free(when);
}
}
}
static void
print_cluster_status(pe_working_set_t * data_set, long options)
{
char *online_nodes = NULL;
char *online_remote_nodes = NULL;
char *online_guest_nodes = NULL;
char *offline_nodes = NULL;
char *offline_remote_nodes = NULL;
size_t online_nodes_len = 0;
size_t online_remote_nodes_len = 0;
size_t online_guest_nodes_len = 0;
size_t offline_nodes_len = 0;
size_t offline_remote_nodes_len = 0;
GListPtr gIter = NULL;
for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
pe_node_t *node = (pe_node_t *) gIter->data;
const char *node_mode = NULL;
char *node_name = NULL;
if (pe__is_guest_node(node)) {
node_name = crm_strdup_printf("%s:%s", node->details->uname, node->details->remote_rsc->container->id);
} else {
node_name = crm_strdup_printf("%s", node->details->uname);
}
if (node->details->unclean) {
if (node->details->online && node->details->unclean) {
node_mode = "UNCLEAN (online)";
} else if (node->details->pending) {
node_mode = "UNCLEAN (pending)";
} else {
node_mode = "UNCLEAN (offline)";
}
} else if (node->details->pending) {
node_mode = "pending";
} else if (node->details->standby_onfail && node->details->online) {
node_mode = "standby (on-fail)";
} else if (node->details->standby) {
if (node->details->online) {
node_mode = "standby";
} else {
node_mode = "OFFLINE (standby)";
}
} else if (node->details->maintenance) {
if (node->details->online) {
node_mode = "maintenance";
} else {
node_mode = "OFFLINE (maintenance)";
}
} else if (node->details->online) {
if (pe__is_guest_node(node)) {
pcmk__add_word(&online_guest_nodes, &online_guest_nodes_len,
node_name);
} else if (pe__is_remote_node(node)) {
pcmk__add_word(&online_remote_nodes, &online_remote_nodes_len,
node_name);
} else {
pcmk__add_word(&online_nodes, &online_nodes_len, node_name);
}
free(node_name);
continue;
} else {
if (pe__is_remote_node(node)) {
pcmk__add_word(&offline_remote_nodes, &offline_remote_nodes_len,
node_name);
} else if (pe__is_guest_node(node)) {
/* ignore offline container nodes */
} else {
pcmk__add_word(&offline_nodes, &offline_nodes_len, node_name);
}
free(node_name);
continue;
}
if (pe__is_guest_node(node)) {
printf("GuestNode %s: %s\n", node_name, node_mode);
} else if (pe__is_remote_node(node)) {
printf("RemoteNode %s: %s\n", node_name, node_mode);
} else if (pcmk__str_eq(node->details->uname, node->details->id, pcmk__str_casei)) {
printf("Node %s: %s\n", node_name, node_mode);
} else {
printf("Node %s (%s): %s\n", node_name, node->details->id, node_mode);
}
free(node_name);
}
if (online_nodes) {
printf("Online: [ %s ]\n", online_nodes);
free(online_nodes);
}
if (offline_nodes) {
printf("OFFLINE: [ %s ]\n", offline_nodes);
free(offline_nodes);
}
if (online_remote_nodes) {
printf("RemoteOnline: [ %s ]\n", online_remote_nodes);
free(online_remote_nodes);
}
if (offline_remote_nodes) {
printf("RemoteOFFLINE: [ %s ]\n", offline_remote_nodes);
free(offline_remote_nodes);
}
if (online_guest_nodes) {
printf("GuestOnline: [ %s ]\n", online_guest_nodes);
free(online_guest_nodes);
}
fprintf(stdout, "\n");
for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
pe_resource_t *rsc = (pe_resource_t *) gIter->data;
if (pcmk_is_set(rsc->flags, pe_rsc_orphan)
&& rsc->role == RSC_ROLE_STOPPED) {
continue;
}
rsc->fns->print(rsc, NULL, pe_print_printf | options, stdout);
}
fprintf(stdout, "\n");
}
static char *
create_action_name(pe_action_t *action)
{
char *action_name = NULL;
const char *prefix = "";
const char *action_host = NULL;
const char *clone_name = NULL;
const char *task = action->task;
if (action->node) {
action_host = action->node->details->uname;
} else if (!pcmk_is_set(action->flags, pe_action_pseudo)) {
action_host = "<none>";
}
if (pcmk__str_eq(action->task, RSC_CANCEL, pcmk__str_casei)) {
prefix = "Cancel ";
task = action->cancel_task;
}
if (action->rsc && action->rsc->clone_name) {
clone_name = action->rsc->clone_name;
}
if (clone_name) {
char *key = NULL;
guint interval_ms = 0;
if (pcmk__guint_from_hash(action->meta,
XML_LRM_ATTR_INTERVAL_MS, 0,
&interval_ms) != pcmk_rc_ok) {
interval_ms = 0;
}
if (pcmk__strcase_any_of(action->task, RSC_NOTIFY, RSC_NOTIFIED, NULL)) {
const char *n_type = g_hash_table_lookup(action->meta, "notify_key_type");
const char *n_task = g_hash_table_lookup(action->meta, "notify_key_operation");
CRM_ASSERT(n_type != NULL);
CRM_ASSERT(n_task != NULL);
key = pcmk__notify_key(clone_name, n_type, n_task);
} else {
key = pcmk__op_key(clone_name, task, interval_ms);
}
if (action_host) {
action_name = crm_strdup_printf("%s%s %s", prefix, key, action_host);
} else {
action_name = crm_strdup_printf("%s%s", prefix, key);
}
free(key);
} else if (pcmk__str_eq(action->task, CRM_OP_FENCE, pcmk__str_casei)) {
const char *op = g_hash_table_lookup(action->meta, "stonith_action");
action_name = crm_strdup_printf("%s%s '%s' %s", prefix, action->task, op, action_host);
} else if (action->rsc && action_host) {
action_name = crm_strdup_printf("%s%s %s", prefix, action->uuid, action_host);
} else if (action_host) {
action_name = crm_strdup_printf("%s%s %s", prefix, action->task, action_host);
} else {
action_name = crm_strdup_printf("%s", action->uuid);
}
if (action_numbers) { // i.e. verbose
char *with_id = crm_strdup_printf("%s (%d)", action_name, action->id);
free(action_name);
action_name = with_id;
}
return action_name;
}
static bool
create_dotfile(pe_working_set_t * data_set, const char *dot_file, gboolean all_actions,
GError **error)
{
GListPtr gIter = NULL;
FILE *dot_strm = fopen(dot_file, "w");
if (dot_strm == NULL) {
g_set_error(error, PCMK__RC_ERROR, errno,
"Could not open %s for writing: %s", dot_file,
pcmk_rc_str(errno));
return false;
}
fprintf(dot_strm, " digraph \"g\" {\n");
for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
pe_action_t *action = (pe_action_t *) gIter->data;
const char *style = "dashed";
const char *font = "black";
const char *color = "black";
char *action_name = create_action_name(action);
crm_trace("Action %d: %s %s %p", action->id, action_name, action->uuid, action);
if (pcmk_is_set(action->flags, pe_action_pseudo)) {
font = "orange";
}
if (pcmk_is_set(action->flags, pe_action_dumped)) {
style = "bold";
color = "green";
} else if ((action->rsc != NULL)
&& !pcmk_is_set(action->rsc->flags, pe_rsc_managed)) {
color = "red";
font = "purple";
if (all_actions == FALSE) {
goto do_not_write;
}
} else if (pcmk_is_set(action->flags, pe_action_optional)) {
color = "blue";
if (all_actions == FALSE) {
goto do_not_write;
}
} else {
color = "red";
CRM_CHECK(!pcmk_is_set(action->flags, pe_action_runnable), ;);
}
pe__set_action_flags(action, pe_action_dumped);
crm_trace("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]",
action_name, style, color, font);
fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\"]\n",
action_name, style, color, font);
do_not_write:
free(action_name);
}
for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) {
pe_action_t *action = (pe_action_t *) gIter->data;
GListPtr gIter2 = NULL;
for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) {
pe_action_wrapper_t *before = (pe_action_wrapper_t *) gIter2->data;
char *before_name = NULL;
char *after_name = NULL;
const char *style = "dashed";
gboolean optional = TRUE;
if (before->state == pe_link_dumped) {
optional = FALSE;
style = "bold";
} else if (pcmk_is_set(action->flags, pe_action_pseudo)
&& (before->type & pe_order_stonith_stop)) {
continue;
} else if (before->type == pe_order_none) {
continue;
} else if (pcmk_is_set(before->action->flags, pe_action_dumped)
&& pcmk_is_set(action->flags, pe_action_dumped)
&& before->type != pe_order_load) {
optional = FALSE;
}
if (all_actions || optional == FALSE) {
before_name = create_action_name(before->action);
after_name = create_action_name(action);
crm_trace("\"%s\" -> \"%s\" [ style = %s]",
before_name, after_name, style);
fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n",
before_name, after_name, style);
free(before_name);
free(after_name);
}
}
}
fprintf(dot_strm, "}\n");
fflush(dot_strm);
fclose(dot_strm);
return true;
}
static int
setup_input(const char *input, const char *output, GError **error)
{
int rc = pcmk_rc_ok;
cib_t *cib_conn = NULL;
xmlNode *cib_object = NULL;
char *local_output = NULL;
if (input == NULL) {
/* Use live CIB */
cib_conn = cib_new();
rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command);
rc = pcmk_legacy2rc(rc);
if (rc == pcmk_rc_ok) {
rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, cib_scope_local | cib_sync_call);
}
cib_conn->cmds->signoff(cib_conn);
cib_delete(cib_conn);
cib_conn = NULL;
if (rc != pcmk_rc_ok) {
rc = pcmk_legacy2rc(rc);
g_set_error(error, PCMK__RC_ERROR, rc,
"Live CIB query failed: %s (%d)", pcmk_rc_str(rc), rc);
return rc;
} else if (cib_object == NULL) {
g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_NOINPUT,
"Live CIB query failed: empty result");
return pcmk_rc_no_input;
}
} else if (pcmk__str_eq(input, "-", pcmk__str_casei)) {
cib_object = filename2xml(NULL);
} else {
cib_object = filename2xml(input);
}
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 pcmk_rc_transform_failed;
}
if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
free_xml(cib_object);
return pcmk_rc_schema_validation;
}
if (output == NULL) {
char *pid = pcmk__getpid_s();
local_output = get_shadow_file(pid);
temp_shadow = strdup(local_output);
output = local_output;
free(pid);
}
rc = write_xml_file(cib_object, output, FALSE);
free_xml(cib_object);
cib_object = NULL;
if (rc < 0) {
rc = pcmk_legacy2rc(rc);
g_set_error(error, PCMK__EXITC_ERROR, CRM_EX_CANTCREAT,
"Could not create '%s': %s", output, pcmk_rc_str(rc));
return rc;
} else {
setenv("CIB_file", output, 1);
free(local_output);
return pcmk_rc_ok;
}
}
static void
profile_one(const char *xml_file, long long repeat, pe_working_set_t *data_set, char *use_date)
{
xmlNode *cib_object = NULL;
clock_t start = 0;
printf("* Testing %s ...", xml_file);
fflush(stdout);
cib_object = filename2xml(xml_file);
start = clock();
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;
}
if (validate_xml(cib_object, NULL, FALSE) != TRUE) {
free_xml(cib_object);
return;
}
for (int i = 0; i < repeat; ++i) {
xmlNode *input = (repeat == 1)? cib_object : copy_xml(cib_object);
data_set->input = input;
get_date(data_set, false, use_date);
pcmk__schedule_actions(data_set, input, NULL);
pe_reset_working_set(data_set);
}
printf(" %.2f secs\n", (clock() - start) / (float) CLOCKS_PER_SEC);
}
#ifndef FILENAME_MAX
# define FILENAME_MAX 512
#endif
static void
profile_all(const char *dir, long long repeat, pe_working_set_t *data_set, char *use_date)
{
struct dirent **namelist;
int file_num = scandir(dir, &namelist, 0, alphasort);
if (file_num > 0) {
struct stat prop;
char buffer[FILENAME_MAX];
while (file_num--) {
if ('.' == namelist[file_num]->d_name[0]) {
free(namelist[file_num]);
continue;
} else if (!pcmk__ends_with_ext(namelist[file_num]->d_name,
".xml")) {
free(namelist[file_num]);
continue;
}
snprintf(buffer, sizeof(buffer), "%s/%s", dir, namelist[file_num]->d_name);
if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) {
profile_one(buffer, repeat, data_set, use_date);
}
free(namelist[file_num]);
}
free(namelist);
}
}
static GOptionContext *
build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
GOptionContext *context = NULL;
GOptionEntry extra_prog_entries[] = {
{ "quiet", 'Q', 0, G_OPTION_ARG_NONE, &(args->quiet),
"Display only essential output",
NULL },
{ NULL }
};
const char *description = "Operation Specification:\n\n"
"The OPSPEC in any command line option is of the form ${resource}_${task}_${interval_in_ms}@${node}=${rc} "
"memcached_monitor_20000@bart.example.com=7, for example). ${rc} is an OCF return code. For more "
"information on these return codes, refer to "
"https://clusterlabs.org/pacemaker/doc/en-US/Pacemaker/2.0/html/Pacemaker_Administration/s-ocf-return-codes.html\n\n"
"Examples:\n\n"
"Pretend a recurring monitor action found memcached stopped on node "
"fred.example.com and, during recovery, that the memcached stop "
"action failed:\n\n"
"\tcrm_simulate -LS --op-inject memcached:0_monitor_20000@bart.example.com=7 "
"--op-fail memcached:0_stop_0@fred.example.com=1 --save-output /tmp/memcached-test.xml\n\n"
"Now see what the reaction to the stop failed would be:\n\n"
"\tcrm_simulate -S --xml-file /tmp/memcached-test.xml\n\n";
context = pcmk__build_arg_context(args, NULL, group, NULL);
pcmk__add_main_args(context, extra_prog_entries);
g_option_context_set_description(context, description);
pcmk__add_arg_group(context, "operations", "Operations:",
"Show operations options", operation_entries);
pcmk__add_arg_group(context, "synthetic", "Synthetic Cluster Events:",
"Show synthetic cluster event options", synthetic_entries);
pcmk__add_arg_group(context, "output", "Output Options:",
"Show output options", output_entries);
pcmk__add_arg_group(context, "source", "Data Source:",
"Show data source options", source_entries);
return context;
}
int
main(int argc, char **argv)
{
GError *error = NULL;
GOptionGroup *output_group = NULL;
gchar **processed_args = NULL;
pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
GOptionContext *context = build_arg_context(args, &output_group);
int rc = pcmk_rc_ok;
pe_working_set_t *data_set = NULL;
xmlNode *input = NULL;
options.xml_file = strdup("-");
crm_log_cli_init("crm_simulate");
processed_args = pcmk__cmdline_preproc(argv, "bdefgiqrtuwxDFGINO");
if (!g_option_context_parse_strv(context, &processed_args, &error)) {
fprintf(stderr, "%s: %s\n", g_get_prgname(), error->message);
goto done;
}
for (int i = 0; i < args->verbosity; i++) {
crm_bump_log_level(argc, argv);
}
if (args->version) {
/* FIXME: When crm_simulate is converted to use formatted output, this can go. */
pcmk__cli_help('v', CRM_EX_USAGE);
goto done;
}
if (optind > argc) {
gchar *help = g_option_context_get_help(context, TRUE, NULL);
fprintf(stderr, "%s", help);
g_free(help);
goto done;
}
if (args->verbosity > 0) {
/* Redirect stderr to stdout so we can grep the output */
close(STDERR_FILENO);
dup2(STDOUT_FILENO, STDERR_FILENO);
action_numbers = TRUE;
}
if (args->quiet) {
quiet = TRUE;
}
data_set = pe_new_working_set();
if (data_set == NULL) {
rc = ENOMEM;
g_set_error(&error, PCMK__RC_ERROR, rc, "Could not allocate working set");
goto done;
}
pe__set_working_set_flags(data_set, pe_flag_no_compat);
if (options.test_dir != NULL) {
profile_all(options.test_dir, options.repeat, data_set, options.use_date);
rc = pcmk_rc_ok;
goto done;
}
rc = setup_input(options.xml_file, options.store ? options.xml_file : options.output_file, &error);
if (rc != pcmk_rc_ok) {
goto done;
}
global_cib = cib_new();
rc = global_cib->cmds->signon(global_cib, crm_system_name, cib_command);
if (rc != pcmk_rc_ok) {
rc = pcmk_legacy2rc(rc);
g_set_error(&error, PCMK__RC_ERROR, rc,
"Could not connect to the CIB: %s", pcmk_rc_str(rc));
goto done;
}
rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call | cib_scope_local);
if (rc != pcmk_rc_ok) {
rc = pcmk_legacy2rc(rc);
g_set_error(&error, PCMK__RC_ERROR, rc,
"Could not get local CIB: %s", pcmk_rc_str(rc));
goto done;
}
data_set->input = input;
get_date(data_set, true, options.use_date);
if(options.xml_file) {
pe__set_working_set_flags(data_set, pe_flag_sanitized);
}
pe__set_working_set_flags(data_set, pe_flag_stdout);
cluster_status(data_set);
if (quiet == FALSE) {
int opts = options.print_pending ? pe_print_pending : 0;
if (pcmk_is_set(data_set->flags, pe_flag_maintenance_mode)) {
quiet_log("\n *** Resource management is DISABLED ***");
quiet_log("\n The cluster will not attempt to start, stop or recover services");
quiet_log("\n");
}
if (data_set->disabled_resources || data_set->blocked_resources) {
quiet_log("%d of %d resource instances DISABLED and %d BLOCKED "
"from further action due to failure\n",
data_set->disabled_resources, data_set->ninstances,
data_set->blocked_resources);
}
quiet_log("\nCurrent cluster status:\n");
print_cluster_status(data_set, opts);
}
if (options.modified) {
quiet_log("Performing requested modifications\n");
modify_configuration(data_set, global_cib, options.quorum, options.watchdog, options.node_up,
options.node_down, options.node_fail, options.op_inject,
options.ticket_grant, options.ticket_revoke, options.ticket_standby,
options.ticket_activate);
rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call);
if (rc != pcmk_rc_ok) {
rc = pcmk_legacy2rc(rc);
g_set_error(&error, PCMK__RC_ERROR, rc,
"Could not get modified CIB: %s", pcmk_rc_str(rc));
goto done;
}
cleanup_calculations(data_set);
data_set->input = input;
get_date(data_set, true, options.use_date);
if(options.xml_file) {
pe__set_working_set_flags(data_set, pe_flag_sanitized);
}
pe__set_working_set_flags(data_set, pe_flag_stdout);
cluster_status(data_set);
}
if (options.input_file != NULL) {
rc = write_xml_file(input, options.input_file, FALSE);
if (rc < 0) {
rc = pcmk_legacy2rc(rc);
g_set_error(&error, PCMK__RC_ERROR, rc,
"Could not create '%s': %s", options.input_file, pcmk_rc_str(rc));
goto done;
}
}
if (options.process || options.simulate) {
crm_time_t *local_date = NULL;
if (show_scores && show_utilization) {
printf("Allocation scores and utilization information:\n");
} else if (show_scores) {
fprintf(stdout, "Allocation scores:\n");
} else if (show_utilization) {
printf("Utilization information:\n");
}
pcmk__schedule_actions(data_set, input, local_date);
input = NULL; /* Don't try and free it twice */
if (options.graph_file != NULL) {
write_xml_file(data_set->graph, options.graph_file, FALSE);
}
if (options.dot_file != NULL) {
if (!create_dotfile(data_set, options.dot_file, options.all_actions, &error)) {
goto done;
}
}
if (quiet == FALSE) {
GListPtr gIter = NULL;
quiet_log("%sTransition Summary:\n", show_scores || show_utilization
|| options.modified ? "\n" : "");
fflush(stdout);
LogNodeActions(data_set, TRUE);
for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
pe_resource_t *rsc = (pe_resource_t *) gIter->data;
LogActions(rsc, data_set, TRUE);
}
}
}
rc = pcmk_rc_ok;
if (options.simulate) {
if (run_simulation(data_set, global_cib, options.op_fail, quiet) != pcmk_rc_ok) {
rc = pcmk_rc_error;
}
if(quiet == FALSE) {
get_date(data_set, true, options.use_date);
quiet_log("\nRevised cluster status:\n");
pe__set_working_set_flags(data_set, pe_flag_stdout);
cluster_status(data_set);
print_cluster_status(data_set, 0);
}
}
done:
if (error != NULL) {
fprintf(stderr, "%s\n", error->message);
g_clear_error(&error);
}
/* There sure is a lot to free in options. */
free(options.dot_file);
free(options.graph_file);
g_free(options.input_file);
g_list_free_full(options.node_up, g_free);
g_list_free_full(options.node_down, g_free);
g_list_free_full(options.node_fail, g_free);
g_list_free_full(options.op_fail, g_free);
g_list_free_full(options.op_inject, g_free);
g_free(options.output_file);
free(options.quorum);
g_free(options.test_dir);
g_list_free_full(options.ticket_grant, g_free);
g_list_free_full(options.ticket_revoke, g_free);
g_list_free_full(options.ticket_standby, g_free);
g_list_free_full(options.ticket_activate, g_free);
free(options.use_date);
free(options.watchdog);
free(options.xml_file);
pcmk__free_arg_context(context);
g_strfreev(processed_args);
if (data_set) {
pe_free_working_set(data_set);
}
if (global_cib) {
global_cib->cmds->signoff(global_cib);
cib_delete(global_cib);
}
fflush(stderr);
if (temp_shadow) {
unlink(temp_shadow);
free(temp_shadow);
}
crm_exit(pcmk_rc2exitc(rc));
}

File Metadata

Mime Type
text/x-c
Expires
Tue, Oct 29, 8:44 PM (1 d, 14 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
942615
Default Alt Text
crm_simulate.c (36 KB)

Event Timeline