diff --git a/tools/Makefile.am b/tools/Makefile.am index 91f4a6338e..53805147c1 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,140 +1,143 @@ # # Copyright (C) 2004-2009 Andrew Beekhof # # 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 program 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 program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # MAINTAINERCLEANFILES = Makefile.in SUBDIRS = shell INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/include \ -I$(top_builddir)/libltdl -I$(top_srcdir)/libltdl COMMONLIBS = \ $(top_builddir)/lib/common/libcrmcommon.la \ $(top_builddir)/lib/cib/libcib.la \ $(CURSESLIBS) $(CLUSTERLIBS) headerdir = $(pkgincludedir)/crm header_HEADERS = attrd.h EXTRA_DIST = $(sbin_SCRIPTS) halibdir = $(CRM_DAEMON_DIR) halib_SCRIPTS = haresources2cib.py hb2openais.sh halib_PROGRAMS = attrd pingd halib_PYTHON = crm_primitive.py hb2openais-helper.py sbin_PROGRAMS = crm_simulate crmadmin cibadmin crm_node crm_attribute crm_resource crm_verify \ crm_uuid crm_shadow attrd_updater crm_diff crm_mon iso8601 if BUILD_SERVICELOG sbin_PROGRAMS += notifyServicelogEvent endif if BUILD_OPENIPMI_SERICELOG sbin_PROGRAMS += ipmiservicelogd endif if BUILD_HELP man8_MANS = $(sbin_PROGRAMS:%=%.8) -%.8: % - echo Creating $@ - help2man --output $@ --no-info --section 8 --name "Part of the Pacemaker cluster resource manager" $(top_builddir)/tools/$< endif sbin_SCRIPTS = crm crm_standby crm_master crm_failcount ## SOURCES #noinst_HEADERS = config.h control.h crmd.h noinst_HEADERS = crmadmin_SOURCES = crmadmin.c crmadmin_LDADD = $(COMMONLIBS) $(CLUSTERLIBS) \ $(top_builddir)/lib/pengine/libpe_status.la crm_uuid_SOURCES = crm_uuid.c crm_uuid_LDADD = $(top_builddir)/lib/common/libcrmcluster.la cibadmin_SOURCES = cibadmin.c cibadmin_LDADD = $(COMMONLIBS) crm_shadow_SOURCES = cib_shadow.c crm_shadow_LDADD = $(COMMONLIBS) crm_node_SOURCES = ccm_epoche.c crm_node_LDADD = $(COMMONLIBS) $(CLUSTERLIBS) \ $(top_builddir)/lib/common/libcrmcluster.la crm_simulate_SOURCES = crm_inject.c +crm_simulate_CFLAGS = -I$(top_srcdir)/pengine + crm_simulate_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/transition/libtransitioner.la crm_diff_SOURCES = xml_diff.c crm_diff_LDADD = $(COMMONLIBS) crm_mon_SOURCES = crm_mon.c crm_mon_LDADD = $(COMMONLIBS) $(SNMPLIBS) $(ESMTPLIBS) -llrm \ $(top_builddir)/lib/pengine/libpe_status.la # Arguments could be made that this should live in crm/pengine crm_verify_SOURCES = crm_verify.c crm_verify_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la crm_attribute_SOURCES = crm_attribute.c crm_attribute_LDADD = $(COMMONLIBS) crm_resource_SOURCES = crm_resource.c crm_resource_LDADD = $(COMMONLIBS) \ $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la iso8601_SOURCES = test.iso8601.c iso8601_LDADD = $(COMMONLIBS) attrd_SOURCES = attrd.c attrd_LDADD = $(COMMONLIBS) $(top_builddir)/lib/common/libcrmcluster.la pingd_SOURCES = pingd.c pingd_LDADD = $(COMMONLIBS) attrd_updater_SOURCES = attrd_updater.c attrd_updater_LDADD = $(COMMONLIBS) if BUILD_SERVICELOG notifyServicelogEvent_SOURCES = notifyServicelogEvent.c notifyServicelogEvent_CFLAGS = `pkg-config --cflags servicelog-1` notifyServicelogEvent_LDFLAGS = `pkg-config --libs servicelog-1` $(top_builddir)/lib/common/libcrmcommon.la endif if BUILD_OPENIPMI_SERICELOG ipmiservicelogd_SOURCES = ipmiservicelogd.c ipmiservicelogd_CFLAGS = `pkg-config --cflags OpenIPMI OpenIPMIposix servicelog-1` ipmiservicelogd_LDFLAGS = `pkg-config --libs OpenIPMI OpenIPMIposix servicelog-1` $(top_builddir)/lib/common/libcrmcommon.la endif +%.8: % + echo Creating $@ + help2man --output $@ --no-info --section 8 --name "Part of the Pacemaker cluster resource manager" $(top_builddir)/tools/$< + clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: .PHONY: install-exec-hook diff --git a/tools/crm_inject.c b/tools/crm_inject.c index e52f983f9b..c4e3722659 100644 --- a/tools/crm_inject.c +++ b/tools/crm_inject.c @@ -1,704 +1,978 @@ /* * Copyright (C) 2009 Andrew Beekhof * * 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include +#include cib_t *global_cib = NULL; GListPtr op_fail = NULL; #define node_template "//"XML_CIB_TAG_STATE"[@uname='%s']" #define rsc_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']" #define op_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s']" /* #define op_template "//"XML_CIB_TAG_STATE"[@uname='%s']//"XML_LRM_TAG_RESOURCE"[@id='%s']/"XML_LRM_TAG_RSC_OP"[@id='%s' and @"XML_LRM_ATTR_CALLID"='%d']" */ #define FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" extern xmlNode * do_calculations( pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); static xmlNode *find_resource(xmlNode *cib_node, const char *resource) { char *xpath = NULL; xmlNode *match = NULL; const char *node = crm_element_value(cib_node, XML_ATTR_UNAME); int max = strlen(rsc_template) + strlen(resource) + strlen(node) + 1; crm_malloc0(xpath, max); snprintf(xpath, max, rsc_template, node, resource); match = get_xpath_object(xpath, cib_node, LOG_DEBUG_2); crm_free(xpath); return match; } static xmlNode *inject_node(cib_t *cib_conn, char *node) { int rc = cib_ok; int max = strlen(rsc_template) + strlen(node) + 1; char *xpath = NULL; xmlNode *cib_object = NULL; crm_malloc0(xpath, max); snprintf(xpath, max, node_template, node); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); if(rc == cib_NOTEXISTS) { char *uuid = NULL; cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); determine_host(cib_conn, &node, &uuid); crm_xml_add(cib_object, XML_ATTR_UUID, uuid); crm_xml_add(cib_object, XML_ATTR_UNAME, node); cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object, cib_sync_call|cib_scope_local); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); } CRM_ASSERT(rc == cib_ok); return cib_object; } static xmlNode *modify_node(cib_t *cib_conn, char *node, gboolean up) { xmlNode *cib_node = inject_node(cib_conn, node); if(up) { crm_xml_add(cib_node, XML_CIB_ATTR_HASTATE, ACTIVESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_YES); crm_xml_add(cib_node, XML_CIB_ATTR_CRMDSTATE, ONLINESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_MEMBER); crm_xml_add(cib_node, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_MEMBER); } else { crm_xml_add(cib_node, XML_CIB_ATTR_HASTATE, DEADSTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO); crm_xml_add(cib_node, XML_CIB_ATTR_CRMDSTATE, OFFLINESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_DOWN); crm_xml_add(cib_node, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_DOWN); } crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name); return cib_node; } static xmlNode *inject_resource(xmlNode *cib_node, const char *resource, const char *rclass, const char *rtype, const char *rprovider) { xmlNode *lrm = NULL; xmlNode *container = NULL; xmlNode *cib_resource = NULL; cib_resource = find_resource(cib_node, resource); if(cib_resource != NULL) { return cib_resource; } /* One day, add query for class, provider, type */ if(rclass == NULL || rtype == NULL) { fprintf(stderr, "Resource %s not found in the status section of %s." " Please supply the class and type to continue\n", resource, ID(cib_node)); return NULL; } else if(safe_str_neq(rclass, "ocf") && safe_str_neq(rclass, "lsb")) { fprintf(stderr, "Invalid class for %s: %s\n", resource, rclass); return NULL; } else if(safe_str_eq(rclass, "ocf") && rprovider == NULL) { fprintf(stderr, "Please specify the provider for resource %s\n", resource); return NULL; } crm_info("Injecting new resource %s into %s '%s'", resource, xmlGetNodePath(cib_node), ID(cib_node)); lrm = first_named_child(cib_node, XML_CIB_TAG_LRM); if(lrm == NULL) { const char *node_uuid = ID(cib_node); lrm = create_xml_node(cib_node, XML_CIB_TAG_LRM); crm_xml_add(lrm, XML_ATTR_ID, node_uuid); } container = first_named_child(lrm, XML_LRM_TAG_RESOURCES); if(container == NULL) { container = create_xml_node(lrm, XML_LRM_TAG_RESOURCES); } cib_resource = create_xml_node(container, XML_LRM_TAG_RESOURCE); crm_xml_add(cib_resource, XML_ATTR_ID, resource); crm_xml_add(cib_resource, XML_AGENT_ATTR_CLASS, rclass); crm_xml_add(cib_resource, XML_AGENT_ATTR_PROVIDER, rprovider); crm_xml_add(cib_resource, XML_ATTR_TYPE, rtype); return cib_resource; } static lrm_op_t *create_op( xmlNode *cib_resource, const char *task, int interval, int outcome) { lrm_op_t *op = NULL; crm_malloc0(op, sizeof(lrm_op_t)); op->app_name = crm_strdup(crm_system_name); op->rsc_id = crm_strdup(ID(cib_resource)); op->interval = interval; op->op_type = crm_strdup(task); op->rc = outcome; op->op_status = 0; op->params = NULL; /* TODO: Fill me in */ op->call_id = 0; xml_child_iter(cib_resource, xop, int tmp = 0; crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); if(tmp > op->call_id) { op->call_id = tmp; } ); op->call_id++; return op; } static xmlNode *inject_op(xmlNode *cib_resource, lrm_op_t *op, int target_rc) { return create_operation_update(cib_resource, op, CRM_FEATURE_SET, target_rc, crm_system_name); } static gboolean exec_pseudo_action(crm_graph_t *graph, crm_action_t *action) { action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static gboolean exec_rsc_action(crm_graph_t *graph, crm_action_t *action) { int rc = 0; lrm_op_t *op = NULL; int target_outcome = 0; const char *rtype = NULL; const char *rclass = NULL; const char *resource = NULL; const char *rprovider = NULL; const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC); xmlNode *cib_op = NULL; xmlNode *cib_node = NULL; xmlNode *cib_object = NULL; xmlNode *cib_resource = NULL; xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); if(safe_str_eq(crm_element_value(action->xml, "operation"), "probe_complete")) { crm_notice("Skipping %s op for %s", crm_element_value(action->xml, "operation"), node); goto done; } if(action_rsc == NULL) { crm_log_xml_err(action->xml, "Bad"); return FALSE; } resource = ID(action_rsc); rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS); rtype = crm_element_value(action_rsc, XML_ATTR_TYPE); rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER); if(target_rc_s != NULL) { target_outcome = crm_parse_int(target_rc_s, "0"); } CRM_ASSERT(global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call|cib_scope_local) == cib_ok); cib_node = inject_node(global_cib, node); CRM_ASSERT(cib_node != NULL); cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider); CRM_ASSERT(cib_resource != NULL); op = convert_graph_action(cib_resource, action, 0, target_outcome); printf(" * Executing action %d: %s_%s_%d on %s\n", action->id, resource, op->op_type, op->interval, node); slist_iter(spec, char, op_fail, lpc, char *key = NULL; crm_malloc0(key, strlen(spec)); snprintf(key, strlen(spec), "%s_%s_%d@%s=", resource, op->op_type, op->interval, node); if(strncasecmp(key, spec, strlen(key)) == 0) { rc = sscanf(spec, "%*[^=]=%d", &op->rc); action->failed = TRUE; graph->abort_priority = INFINITY; printf("\tPretending action %d failed with rc=%d\n", action->id, op->rc); break; } ); cib_op = inject_op(cib_resource, op, target_outcome); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); done: action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static gboolean exec_crmd_action(crm_graph_t *graph, crm_action_t *action) { action->confirmed = TRUE; update_graph(graph, action); return TRUE; } #define STATUS_PATH_MAX 512 static gboolean exec_stonith_action(crm_graph_t *graph, crm_action_t *action) { int rc = 0; char xpath[STATUS_PATH_MAX]; char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); xmlNode *cib_node = modify_node(global_cib, target, FALSE); crm_xml_add(cib_node, XML_ATTR_ORIGIN, __FUNCTION__); CRM_ASSERT(cib_node != NULL); printf(" * Fencing %s\n", target); rc = global_cib->cmds->replace(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_TAG_TRANSIENT_NODEATTRS); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static void print_cluster_status(pe_working_set_t *data_set) { char *online_nodes = NULL; char *offline_nodes = NULL; slist_iter(node, node_t, data_set->nodes, lpc2, const char *node_mode = NULL; 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->online) { node_mode = "online"; online_nodes = add_list_element(online_nodes, node->details->uname); continue; } else { node_mode = "OFFLINE"; offline_nodes = add_list_element(offline_nodes, node->details->uname); continue; } if(safe_str_eq(node->details->uname, node->details->id)) { printf("Node %s: %s\n", node->details->uname, node_mode); } else { printf("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode); } ); if(online_nodes) { printf("Online: [%s ]\n", online_nodes); crm_free(online_nodes); } if(offline_nodes) { printf("OFFLINE: [%s ]\n", offline_nodes); crm_free(offline_nodes); } fprintf(stdout, "\n"); slist_iter(rsc, resource_t, data_set->resources, lpc, if(is_set(rsc->flags, pe_rsc_orphan) && rsc->role == RSC_ROLE_STOPPED) { continue; } rsc->fns->print(rsc, NULL, pe_print_printf, stdout); ); fprintf(stdout, "\n"); } +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(action->pseudo) { + action_name = crm_strdup(action->uuid); + + } else { + action_host = ""; + 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, ' '); + crm_free(tmp_action_name); + } + + return action_name; +} + +static void +create_dotfile(pe_working_set_t *data_set, const char *dot_file, gboolean all_actions) +{ + FILE *dot_strm = fopen(dot_file, "w"); + if(dot_strm == NULL) { + crm_perror(LOG_ERR,"Could not open %s for writing", dot_file); + return; + } + + fprintf(dot_strm, " digraph \"g\" {\n"); + slist_iter( + action, action_t, data_set->actions, lpc, + + const char *style = "filled"; + const char *font = "black"; + const char *color = "black"; + const char *fill = NULL; + char *action_name = create_action_name(action); + crm_debug_3("Action %d: %p", action->id, action); + + if(action->pseudo) { + font = "orange"; + } + + style = "dashed"; + if(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(action->optional) { + color = "blue"; + if(all_actions == FALSE) { + goto dont_write; + } + + } else { + color = "red"; + CRM_CHECK(action->runnable == FALSE, ;); + } + + action->dumped = TRUE; + fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]\n", + action_name, style, color, font, fill?"fillcolor=":"", fill?fill:""); + dont_write: + crm_free(action_name); + ); + + + slist_iter( + action, action_t, data_set->actions, lpc, + slist_iter( + before, action_wrapper_t, action->actions_before, lpc2, + 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(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(action->dumped && before->action->dumped) { + optional = FALSE; + } + + if(all_actions || optional == FALSE) { + before_name = create_action_name(before->action); + after_name = create_action_name(action); + fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n", + before_name, after_name, style); + crm_free(before_name); + crm_free(after_name); + } + ); + ); + fprintf(dot_strm, "}\n"); + if(dot_strm != NULL) { + fflush(dot_strm); + fclose(dot_strm); + } +} + + static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, - {"verbose", 0, 0, 'V', "\tIncrease debug output\n"}, - - {"run", 0, 0, 'r', "Perform a simulation and populate the status section"}, - - {"-spacer-", 0, 0, '-', "\nNode Events:"}, - {"node-up", 1, 0, 'U', "Bring a node online"}, - {"node-down",1, 0, 'D', "Bring a node offline"}, - {"node-fail",1, 0, 'F', "Mark a node as failed"}, - - {"-spacer-", 0, 0, '-', "\nResource Events:"}, - {"op-inject",1, 0, 'i', "\t$node;$rsc_$task_$interval;$rc - Inject the specified task before running the simulation"}, - {"op-fail", 1, 0, 'f', "\t$node;$rsc_$task_$interval;$rc - Fail the specified task while running the simulation"}, - - {"-spacer-", 0, 0, '-', "\nAdditional Options:"}, - {"set-date", 1, 0, 'd', "Set date"}, - {"quorum", 1, 0, 'q', "Specify a value for quorum"}, - - {"-spacer-",0, 0, '-', "\nData Source:"}, - {"live-check", 0, 0, 'L', "Connect to the CIB and use the current contents as input"}, - {"xml-file", 1, 0, 'x', "Retrieve XML from the named file"}, - {"xml-pipe", 0, 0, 'p', "Retrieve XML from stdin"}, + {"quiet", 0, 0, 'Q', "\tDisplay only essentialoutput"}, + {"verbose", 0, 0, 'V', "\tIncrease debug output"}, + + {"-spacer-", 0, 0, '-', "\nOperations:"}, + {"run", 0, 0, 'R', "\tDetermine the cluster's response to the given configuration and status"}, + {"simulate", 0, 0, 'S', "Simulate the transition's execution and display the resulting cluster status"}, + {"in-place", 0, 0, 'X', "Simulate the transition's execution and store the result back to the input file"}, + {"show-scores", 0, 0, 's', "Show allocation scores"}, + + {"-spacer-", 0, 0, '-', "\nSynthetic Cluster Events:"}, + {"node-up", 1, 0, 'u', "\tBring a node online"}, + {"node-down", 1, 0, 'd', "\tTake a node offline"}, + {"node-fail", 1, 0, 'f', "\tMark a node as failed"}, + {"op-inject", 1, 0, 'i', "\t$node;$rsc_$task_$interval;$rc - Inject the specified task before running the simulation"}, + {"set-datetime", 1, 0, 't', "Set date/time"}, + {"quorum", 1, 0, 'q', "\tSpecify a value for quorum"}, + + {"-spacer-", 0, 0, '-', "\nOutput Options:"}, + + {"save-input", 1, 0, 'I', "\tSave the input configuration to the named file"}, + {"save-output", 1, 0, 'O', "Save the output configuration 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"}, + {"all-actions", 0, 0, 'a', "\tDisplay all possible actions in the DOT graph - even ones not part of the transition"}, + + {"-spacer-", 0, 0, '-', "\nData Source:"}, + {"live-check", 0, 0, 'L', "\tConnect to the CIB and use the current contents as input"}, + {"xml-file", 1, 0, 'x', "\tRetrieve XML from the named file"}, + {"xml-pipe", 0, 0, 'p', "\tRetrieve XML from stdin"}, {0, 0, 0, 0} }; +#define quiet_log(fmt, args...) do { \ + if(quiet == FALSE) { \ + printf(fmt , ##args); \ + } \ + } while(0) + int main(int argc, char ** argv) { int rc = 0; cib_t *cib_conn = NULL; + guint modified = 0; + + gboolean quiet = FALSE; + gboolean store = FALSE; gboolean process = FALSE; - const char *quorum = NULL; + gboolean verbose = FALSE; + gboolean simulate = FALSE; + gboolean all_actions = FALSE; + pe_working_set_t data_set; ha_time_t *a_date = NULL; + + const char *quorum = NULL; + const char *dot_file = NULL; + const char *graph_file = NULL; + const char *input_file = NULL; + const char *output_file = NULL; int flag = 0; int index = 0; int argerr = 0; char *use_date = NULL; lrm_op_t *op = NULL; xmlNode *cib_object = NULL; xmlNode *cib_node = NULL; xmlNode *cib_resource = NULL; xmlNode *cib_op = NULL; GListPtr node_up = NULL; GListPtr node_down = NULL; GListPtr node_fail = NULL; GListPtr op_inject = NULL; - const char *xml_file = NULL; + const char *xml_file = "-"; - crm_log_init("crm_inject", LOG_NOTICE, FALSE, FALSE, argc, argv); - crm_set_options("?$Vpx:U:D:F:i:f:r", "[--run] [additional options]", - long_options, "Tool for injecting tasks into a configuration"); + crm_log_init("crm_inject", LOG_ERR, FALSE, FALSE, argc, argv); + crm_set_options("?$VQx:Lpu:d:f:i:RSXD:G:I:O:sa", "datasource operation [additional options]", + long_options, "Tool for simulating the cluster's response to events"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch(flag) { case 'V': + verbose = TRUE; alter_debug(DEBUG_INC); + cl_log_enable_stderr(TRUE); break; case '?': case '$': crm_help(flag, LSB_EXIT_OK); break; case 'p': xml_file = "-"; break; + case 'Q': + quiet = TRUE; + break; + case 'L': + xml_file = NULL; + break; case 'x': xml_file = optarg; break; - case 'U': + case 'u': + modified++; node_up = g_list_append(node_up, optarg); break; - case 'D': + case 'd': + modified++; node_down = g_list_append(node_down, optarg); break; - case 'F': + case 'f': + modified++; node_fail = g_list_append(node_fail, optarg); break; case 'i': + modified++; op_inject = g_list_append(op_inject, optarg); break; - case 'f': - op_fail = g_list_append(op_fail, optarg); - break; case 'q': + modified++; quorum = optarg; + break; + case 'a': + all_actions = TRUE; + break; + case 's': + process = TRUE; + show_scores = TRUE; break; - case 'r': + case 'S': process = TRUE; + simulate = TRUE; + break; + case 'X': + store = TRUE; + process = TRUE; + simulate = TRUE; + break; + case 'R': + process = TRUE; + break; + case 'D': + dot_file = optarg; + break; + case 'G': + graph_file = optarg; + break; + case 'I': + input_file = optarg; + break; + case 'O': + store = TRUE; + simulate = TRUE; + output_file = optarg; break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } if(xml_file == NULL) { /* Use live CIB */ } else if(safe_str_eq(xml_file, "-")) { crm_err("Piping from stdin is not yet supported"); return 1; /* cib_object = filename2xml(NULL); */ /* write to a temp file */ } else { setenv("CIB_file", xml_file, 1); } + if(output_file || store == FALSE) { + xmlNode *output = NULL; + + if(output_file == NULL) { + char *pid = crm_itoa(getpid()); + output_file = get_shadow_file(pid); + } + + cib_conn = cib_new_no_shadow(); + rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); + if(rc == cib_ok) { + rc = cib_conn->cmds->query(cib_conn, NULL, &output, cib_sync_call); + } + if(rc != cib_ok) { + fprintf(stderr, "Could not connect to the CIB: %s\n", cib_error2string(rc)); + return rc; + } + + rc = write_xml_file(output, output_file, FALSE); + if(rc < 0) { + fprintf(stderr, "Could not create '%s': %s\n", output_file, strerror(errno)); + return rc; + } + setenv("CIB_file", output_file, 1); + if(simulate) { + quiet_log("\nUsing %s for simulation results\n", output_file); + } + rc = cib_conn->cmds->signoff(cib_conn); + cib_delete(cib_conn); + } + cib_conn = cib_new(); global_cib = cib_conn; cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); - if(op_inject || process) { + if(quiet == FALSE) { rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); set_working_set_defaults(&data_set); data_set.input = cib_object; data_set.now = new_ha_date(TRUE); cluster_status(&data_set); - fprintf(stdout, "\nInitial cluster status:\n\n"); + quiet_log("\nCurrent cluster status:\n"); print_cluster_status(&data_set); + if(process == FALSE && modified == FALSE) { + return 0; + } } + + if(modified || use_date) { + quiet_log("Performing requested %s modifications\n", store?"permanent":"temporary"); + } - printf("Performing requested modifications\n"); if(use_date != NULL) { a_date = parse_date(&use_date); - printf(" + Setting effective cluster time: %s", use_date); + quiet_log(" + Setting effective cluster time: %s", use_date); log_date(LOG_WARNING, "Set fake 'now' to", a_date, ha_log_date|ha_log_time); } if(quorum) { xmlNode *top = create_xml_node(NULL, XML_TAG_CIB); - printf(" + Setting quorum: %s\n", quorum); + quiet_log(" + Setting quorum: %s\n", quorum); /* crm_xml_add(top, XML_ATTR_DC_UUID, dc_uuid); */ crm_xml_add(top, XML_ATTR_HAVE_QUORUM, quorum); rc = global_cib->cmds->modify(cib_conn, NULL, top, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); } slist_iter(node, char, node_up, lpc, - printf(" + Bringing node %s online\n", node); + quiet_log(" + Bringing node %s online\n", node); cib_node = modify_node(cib_conn, node, TRUE); CRM_ASSERT(cib_node != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); ); slist_iter(node, char, node_down, lpc, - printf(" + Taking node %s offline\n", node); + quiet_log(" + Taking node %s offline\n", node); cib_node = modify_node(cib_conn, node, FALSE); CRM_ASSERT(cib_node != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); ); slist_iter(node, char, node_fail, lpc, - printf(" + Failing node %s\n", node); + quiet_log(" + Failing node %s\n", node); cib_node = modify_node(cib_conn, node, TRUE); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO); CRM_ASSERT(cib_node != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); ); slist_iter(spec, char, op_inject, lpc, int rc = 0; int outcome = 0; int interval = 0; char *key = NULL; char *node = NULL; char *task = NULL; char *resource = NULL; const char *rtype = NULL; const char *rclass = NULL; const char *rprovider = NULL; resource_t *rsc = NULL; - printf(" + Injecting %s into the configuration\n", spec); + quiet_log(" + Injecting %s into the configuration\n", spec); crm_malloc0(key, strlen(spec)); crm_malloc0(node, strlen(spec)); rc = sscanf(spec, "%[^@]@%[^=]=%d", key, node, &outcome); CRM_CHECK(rc == 3, fprintf(stderr, "Invalid operation spec: %s. Only found %d fields\n", spec, rc); continue); parse_op_key(key, &resource, &task, &interval); rsc = pe_find_resource(data_set.resources, resource); CRM_CHECK(rsc != NULL, fprintf(stderr, "Invalid resource name: %s\n", resource); continue); rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); rprovider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); cib_node = inject_node(cib_conn, node); CRM_ASSERT(cib_node != NULL); cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider); CRM_ASSERT(cib_resource != NULL); op = create_op(cib_resource, task, interval, outcome); CRM_ASSERT(op != NULL); cib_op = inject_op(cib_resource, op, 0); CRM_ASSERT(cib_op != NULL); rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); ); if(process) { crm_graph_t *transition = NULL; enum transition_status graph_rc = -1; crm_graph_functions_t exec_fns = { exec_pseudo_action, exec_rsc_action, exec_crmd_action, exec_stonith_action, }; - - set_graph_functions(&exec_fns); - printf("\nExecuting cluster transition\n"); + set_graph_functions(&exec_fns); + + if(show_scores) { + printf("Allocation scores:\n"); + } + rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); do_calculations(&data_set, cib_object, a_date); - transition = unpack_graph(data_set.graph, crm_system_name); - transition->batch_limit = 0; + if(show_scores) { + printf("\n"); + } - print_graph(LOG_DEBUG, transition); - do { + if(graph_file != NULL) { + char *msg_buffer = dump_xml_formatted(data_set.graph); + 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); + } + crm_free(msg_buffer); + } + + if(dot_file != NULL) { + create_dotfile(&data_set, dot_file, all_actions); + } + + if(quiet == FALSE) { + int level = crm_log_level; + if(level < LOG_NOTICE) { + crm_log_level = LOG_NOTICE; + } + + quiet_log("Transition Summary:\n"); + cl_log_enable_stderr(TRUE); + slist_iter( + rsc, resource_t, data_set.resources, lpc, + LogActions(rsc, &data_set); + ); + + crm_log_level = level; + cl_log_enable_stderr(verbose); + } + + if(simulate) { + quiet_log("\nExecuting cluster transition:\n"); + transition = unpack_graph(data_set.graph, crm_system_name); + print_graph(LOG_DEBUG, transition); + + do { graph_rc = run_graph(transition); - } while(graph_rc == transition_active); - - if(graph_rc != transition_complete) { - printf("Transition failed: %s\n", transition_status(graph_rc)); + } while(graph_rc == transition_active); + + if(graph_rc != transition_complete) { + fprintf(stderr, "Transition failed: %s\n", transition_status(graph_rc)); print_graph(LOG_ERR, transition); + } + destroy_graph(transition); + CRM_CHECK(graph_rc == transition_complete, fprintf(stderr, "An invalid transition was produced")); } - destroy_graph(transition); - CRM_CHECK(graph_rc == transition_complete, crm_err("An invalid transition was produced")); - } - + } rc = cib_conn->cmds->query(cib_conn, NULL, &cib_object, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); - printf("\nCalculating revised cluster status\n"); - set_working_set_defaults(&data_set); - data_set.input = cib_object; - data_set.now = a_date; - - cluster_status(&data_set); - print_cluster_status(&data_set); + if(quiet) { + } else if(simulate || (process == FALSE && modified == 0)) { + quiet_log("\nRevised cluster status:\n"); + set_working_set_defaults(&data_set); + data_set.input = cib_object; + data_set.now = a_date; + + cluster_status(&data_set); + print_cluster_status(&data_set); + } rc = cib_conn->cmds->signoff(cib_conn); + cib_delete(cib_conn); fflush(stderr); return 0; }