diff --git a/tools/Makefile.am b/tools/Makefile.am index 5142b29572..74b69e3cc7 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,147 +1,149 @@ # # 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. # include $(top_srcdir)/Makefile.common COMMONLIBS = \ $(top_builddir)/lib/common/libcrmcommon.la \ $(top_builddir)/lib/cib/libcib.la \ $(CURSESLIBS) $(CLUSTERLIBS) headerdir = $(pkgincludedir)/crm header_HEADERS = pcmkdir = $(datadir)/$(PACKAGE) pcmk_DATA = report.common report.collector sbin_SCRIPTS = crm_report crm_standby crm_master crm_failcount if BUILD_CIBSECRETS sbin_SCRIPTS += cibsecret endif EXTRA_DIST = $(sbin_SCRIPTS) sbin_PROGRAMS = crm_simulate crmadmin cibadmin crm_node crm_attribute crm_resource crm_verify \ crm_shadow attrd_updater crm_diff crm_mon iso8601 crm_ticket crm_error testdir = $(datadir)/$(PACKAGE)/tests/cli test_SCRIPTS = regression.sh test_DATA = regression.dates.exp regression.tools.exp regression.acls.exp if BUILD_HEARTBEAT_SUPPORT sbin_PROGRAMS += crm_uuid endif if BUILD_SERVICELOG sbin_PROGRAMS += notifyServicelogEvent endif if BUILD_OPENIPMI_SERVICELOG sbin_PROGRAMS += ipmiservicelogd endif ## SOURCES noinst_HEADERS = MAN8DEPS = crm_attribute crm_node crmadmin_SOURCES = crmadmin.c crmadmin_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(COMMONLIBS) $(CLUSTERLIBS) crm_error_SOURCES = crm_error.c crm_error_LDADD = $(COMMONLIBS) crm_uuid_SOURCES = crm_uuid.c crm_uuid_LDADD = $(COMMONLIBS) $(top_builddir)/lib/cluster/libcrmcluster.la cibadmin_SOURCES = cibadmin.c cibadmin_LDADD = $(COMMONLIBS) crm_shadow_SOURCES = cib_shadow.c crm_shadow_LDADD = $(COMMONLIBS) crm_node_SOURCES = crm_node.c crm_node_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la \ $(COMMONLIBS) $(CLUSTERLIBS) -crm_simulate_SOURCES = crm_simulate.c +crm_simulate_SOURCES = crm_simulate.c fake_transition.c crm_simulate_CFLAGS = -I$(top_srcdir)/pengine crm_simulate_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la \ $(top_builddir)/lib/cib/libcib.la \ $(top_builddir)/lib/lrmd/liblrmd.la \ $(top_builddir)/lib/transition/libtransitioner.la \ $(COMMONLIBS) crm_diff_SOURCES = xml_diff.c crm_diff_LDADD = $(COMMONLIBS) crm_mon_SOURCES = crm_mon.c crm_mon_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/lib/fencing/libstonithd.la \ $(top_builddir)/pengine/libpengine.la \ $(COMMONLIBS) $(SNMPLIBS) $(ESMTPLIBS) # Arguments could be made that this should live in crm/pengine crm_verify_SOURCES = crm_verify.c crm_verify_LDADD = $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la \ $(COMMONLIBS) crm_attribute_SOURCES = crm_attribute.c crm_attribute_LDADD = $(top_builddir)/lib/cluster/libcrmcluster.la $(COMMONLIBS) -crm_resource_SOURCES = crm_resource.c -crm_resource_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ - $(top_builddir)/lib/lrmd/liblrmd.la \ - $(top_builddir)/lib/services/libcrmservice.la \ - $(top_builddir)/lib/pengine/libpe_status.la \ - $(top_builddir)/pengine/libpengine.la \ +crm_resource_SOURCES = crm_resource.c fake_transition.c +crm_resource_CFLAGS = -I$(top_srcdir)/pengine +crm_resource_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ + $(top_builddir)/lib/lrmd/liblrmd.la \ + $(top_builddir)/lib/services/libcrmservice.la \ + $(top_builddir)/lib/pengine/libpe_status.la \ + $(top_builddir)/pengine/libpengine.la \ + $(top_builddir)/lib/transition/libtransitioner.la \ $(COMMONLIBS) iso8601_SOURCES = test.iso8601.c iso8601_LDADD = $(COMMONLIBS) attrd_updater_SOURCES = attrd_updater.c attrd_updater_LDADD = $(COMMONLIBS) crm_ticket_SOURCES = crm_ticket.c crm_ticket_LDADD = $(top_builddir)/lib/pengine/libpe_rules.la \ $(top_builddir)/lib/pengine/libpe_status.la \ $(top_builddir)/pengine/libpengine.la \ $(COMMONLIBS) if BUILD_SERVICELOG notifyServicelogEvent_SOURCES = notifyServicelogEvent.c notifyServicelogEvent_CFLAGS = $(SERVICELOG_CFLAGS) notifyServicelogEvent_LDADD = $(top_builddir)/lib/common/libcrmcommon.la $(SERVICELOG_LIBS) endif if BUILD_OPENIPMI_SERVICELOG ipmiservicelogd_SOURCES = ipmiservicelogd.c ipmiservicelogd_CFLAGS = $(OPENIPMI_SERVICELOG_CFLAGS) $(SERVICELOG_CFLAGS) ipmiservicelogd_LDFLAGS = $(top_builddir)/lib/common/libcrmcommon.la $(OPENIPMI_SERVICELOG_LIBS) $(SERVICELOG_LIBS) endif clean-generic: rm -f *.log *.debug *.xml *~ install-exec-local: uninstall-local: .PHONY: install-exec-hook diff --git a/tools/crm_resource.c b/tools/crm_resource.c index ff5effd05d..882eae2c5d 100644 --- a/tools/crm_resource.c +++ b/tools/crm_resource.c @@ -1,2310 +1,2608 @@ /* * Copyright (C) 2004 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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "fake_transition.h" +extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now); + bool scope_master = FALSE; gboolean do_force = FALSE; gboolean BE_QUIET = FALSE; const char *attr_set_type = XML_TAG_ATTR_SETS; char *host_id = NULL; const char *rsc_id = NULL; const char *host_uname = NULL; const char *prop_name = NULL; const char *prop_value = NULL; const char *rsc_type = NULL; const char *prop_id = NULL; const char *prop_set = NULL; char *move_lifetime = NULL; char rsc_cmd = 'L'; const char *rsc_long_cmd = NULL; char *our_pid = NULL; crm_ipc_t *crmd_channel = NULL; char *xml_file = NULL; int cib_options = cib_sync_call; int crmd_replies_needed = 1; /* The welcome message */ GMainLoop *mainloop = NULL; gboolean print_pending = FALSE; extern void cleanup_alloc_calculations(pe_working_set_t * data_set); #define CMD_ERR(fmt, args...) do { \ crm_warn(fmt, ##args); \ fprintf(stderr, fmt, ##args); \ } while(0) #define message_timeout_ms 60*1000 static gboolean resource_ipc_timeout(gpointer data) { fprintf(stderr, "No messages received in %d seconds.. aborting\n", (int)message_timeout_ms / 1000); crm_err("No messages received in %d seconds", (int)message_timeout_ms / 1000); return crm_exit(-1); } static void resource_ipc_connection_destroy(gpointer user_data) { crm_info("Connection to CRMd was terminated"); crm_exit(1); } static void start_mainloop(void) { if (crmd_replies_needed == 0) { return; } mainloop = g_main_new(FALSE); fprintf(stderr, "Waiting for %d replies from the CRMd", crmd_replies_needed); crm_debug("Waiting for %d replies from the CRMd", crmd_replies_needed); g_timeout_add(message_timeout_ms, resource_ipc_timeout, NULL); g_main_run(mainloop); } static int resource_ipc_callback(const char *buffer, ssize_t length, gpointer userdata) { xmlNode *msg = string2xml(buffer); fprintf(stderr, "."); crm_log_xml_trace(msg, "[inbound]"); crmd_replies_needed--; if (crmd_replies_needed == 0) { fprintf(stderr, " OK\n"); crm_debug("Got all the replies we expected"); return crm_exit(pcmk_ok); } free_xml(msg); return 0; } struct ipc_client_callbacks crm_callbacks = { .dispatch = resource_ipc_callback, .destroy = resource_ipc_connection_destroy, }; static int do_find_resource(const char *rsc, resource_t * the_rsc, pe_working_set_t * data_set) { int found = 0; GListPtr lpc = NULL; if (the_rsc == NULL) { the_rsc = pe_find_resource(data_set->resources, rsc); } if (the_rsc == NULL) { return -ENXIO; } if (the_rsc->variant >= pe_clone) { GListPtr gIter = the_rsc->children; for (; gIter != NULL; gIter = gIter->next) { found += do_find_resource(rsc, gIter->data, data_set); } return found; } for (lpc = the_rsc->running_on; lpc != NULL; lpc = lpc->next) { node_t *node = (node_t *) lpc->data; crm_trace("resource %s is running on: %s", rsc, node->details->uname); if (BE_QUIET) { fprintf(stdout, "%s\n", node->details->uname); } else { const char *state = ""; if (the_rsc->variant < pe_clone && the_rsc->fns->state(the_rsc, TRUE) == RSC_ROLE_MASTER) { state = "Master"; } fprintf(stdout, "resource %s is running on: %s %s\n", rsc, node->details->uname, state); } found++; } if (BE_QUIET == FALSE && found == 0) { fprintf(stderr, "resource %s is NOT running\n", rsc); } return 0; } #define cons_string(x) x?x:"NA" static void print_cts_constraints(pe_working_set_t * data_set) { xmlNode *xml_obj = NULL; xmlNode *lifetime = NULL; xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input); for (xml_obj = __xml_first_child(cib_constraints); xml_obj != NULL; xml_obj = __xml_next(xml_obj)) { const char *id = crm_element_value(xml_obj, XML_ATTR_ID); if (id == NULL) { continue; } lifetime = first_named_child(xml_obj, "lifetime"); if (test_ruleset(lifetime, NULL, data_set->now) == FALSE) { continue; } if (safe_str_eq(XML_CONS_TAG_RSC_DEPEND, crm_element_name(xml_obj))) { printf("Constraint %s %s %s %s %s %s %s\n", crm_element_name(xml_obj), cons_string(crm_element_value(xml_obj, XML_ATTR_ID)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET)), cons_string(crm_element_value(xml_obj, XML_RULE_ATTR_SCORE)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE)), cons_string(crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE))); } else if (safe_str_eq(XML_CONS_TAG_RSC_LOCATION, crm_element_name(xml_obj))) { /* unpack_location(xml_obj, data_set); */ } } } static void print_cts_rsc(resource_t * rsc) { GListPtr lpc = NULL; const char *host = NULL; gboolean needs_quorum = TRUE; const char *rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); const char *rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); const char *rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); if (safe_str_eq(rclass, "stonith")) { xmlNode *op = NULL; needs_quorum = FALSE; for (op = __xml_first_child(rsc->ops_xml); op != NULL; op = __xml_next(op)) { if (crm_str_eq((const char *)op->name, "op", TRUE)) { const char *name = crm_element_value(op, "name"); if (safe_str_neq(name, CRMD_ACTION_START)) { const char *value = crm_element_value(op, "requires"); if (safe_str_eq(value, "nothing")) { needs_quorum = FALSE; } break; } } } } if (rsc->running_on != NULL && g_list_length(rsc->running_on) == 1) { node_t *tmp = rsc->running_on->data; host = tmp->details->uname; } printf("Resource: %s %s %s %s %s %s %s %s %d %lld 0x%.16llx\n", crm_element_name(rsc->xml), rsc->id, rsc->clone_name ? rsc->clone_name : rsc->id, rsc->parent ? rsc->parent->id : "NA", rprov ? rprov : "NA", rclass, rtype, host ? host : "NA", needs_quorum, rsc->flags, rsc->flags); for (lpc = rsc->children; lpc != NULL; lpc = lpc->next) { resource_t *child = (resource_t *) lpc->data; print_cts_rsc(child); } } static void print_raw_rsc(resource_t * rsc) { GListPtr lpc = NULL; GListPtr children = rsc->children; if (children == NULL) { printf("%s\n", rsc->id); } for (lpc = children; lpc != NULL; lpc = lpc->next) { resource_t *child = (resource_t *) lpc->data; print_raw_rsc(child); } } static int do_find_resource_list(pe_working_set_t * data_set, gboolean raw) { int found = 0; GListPtr lpc = NULL; int opts = pe_print_printf | pe_print_rsconly; if (print_pending) { opts |= pe_print_pending; } for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { resource_t *rsc = (resource_t *) lpc->data; if (is_set(rsc->flags, pe_rsc_orphan) && rsc->fns->active(rsc, TRUE) == FALSE) { continue; } rsc->fns->print(rsc, NULL, opts, stdout); found++; } if (found == 0) { printf("NO resources configured\n"); return -ENXIO; } return 0; } static resource_t * find_rsc_or_clone(const char *rsc, pe_working_set_t * data_set) { resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if (the_rsc == NULL) { char *as_clone = crm_concat(rsc, "0", ':'); the_rsc = pe_find_resource(data_set->resources, as_clone); free(as_clone); } return the_rsc; } static int dump_resource(const char *rsc, pe_working_set_t * data_set, gboolean expanded) { char *rsc_xml = NULL; resource_t *the_rsc = find_rsc_or_clone(rsc, data_set); int opts = pe_print_printf; if (the_rsc == NULL) { return -ENXIO; } if (print_pending) { opts |= pe_print_pending; } the_rsc->fns->print(the_rsc, NULL, opts, stdout); if (expanded) { rsc_xml = dump_xml_formatted(the_rsc->xml); } else { if (the_rsc->orig_xml) { rsc_xml = dump_xml_formatted(the_rsc->orig_xml); } else { rsc_xml = dump_xml_formatted(the_rsc->xml); } } fprintf(stdout, "%sxml:\n%s\n", expanded ? "" : "raw ", rsc_xml); free(rsc_xml); return 0; } static int dump_resource_attr(const char *rsc, const char *attr, pe_working_set_t * data_set) { int rc = -ENXIO; node_t *current = NULL; GHashTable *params = NULL; resource_t *the_rsc = find_rsc_or_clone(rsc, data_set); const char *value = NULL; if (the_rsc == NULL) { return -ENXIO; } if (g_list_length(the_rsc->running_on) == 1) { current = the_rsc->running_on->data; } else if (g_list_length(the_rsc->running_on) > 1) { CMD_ERR("%s is active on more than one node," " returning the default value for %s\n", the_rsc->id, crm_str(value)); } params = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if (safe_str_eq(attr_set_type, XML_TAG_ATTR_SETS)) { get_rsc_attributes(params, the_rsc, current, data_set); } else if (safe_str_eq(attr_set_type, XML_TAG_META_SETS)) { get_meta_attributes(params, the_rsc, current, data_set); } else { unpack_instance_attributes(data_set->input, the_rsc->xml, XML_TAG_UTILIZATION, NULL, params, NULL, FALSE, data_set->now); } crm_debug("Looking up %s in %s", attr, the_rsc->id); value = g_hash_table_lookup(params, attr); if (value != NULL) { fprintf(stdout, "%s\n", value); rc = 0; } else { CMD_ERR("Attribute '%s' not found for '%s'\n", attr, the_rsc->id); } g_hash_table_destroy(params); return rc; } static int find_resource_attr(cib_t * the_cib, const char *attr, const char *rsc, const char *set_type, const char *set_name, const char *attr_id, const char *attr_name, char **value) { int offset = 0; static int xpath_max = 1024; int rc = pcmk_ok; xmlNode *xml_search = NULL; char *xpath_string = NULL; CRM_ASSERT(value != NULL); *value = NULL; if(the_cib == NULL) { return -ENOTCONN; } xpath_string = calloc(1, xpath_max); offset += snprintf(xpath_string + offset, xpath_max - offset, "%s", get_object_path("resources")); offset += snprintf(xpath_string + offset, xpath_max - offset, "//*[@id=\"%s\"]", rsc); if (set_type) { offset += snprintf(xpath_string + offset, xpath_max - offset, "/%s", set_type); if (set_name) { offset += snprintf(xpath_string + offset, xpath_max - offset, "[@id=\"%s\"]", set_name); } } offset += snprintf(xpath_string + offset, xpath_max - offset, "//nvpair["); if (attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, "@id=\"%s\"", attr_id); } if (attr_name) { if (attr_id) { offset += snprintf(xpath_string + offset, xpath_max - offset, " and "); } offset += snprintf(xpath_string + offset, xpath_max - offset, "@name=\"%s\"", attr_name); } offset += snprintf(xpath_string + offset, xpath_max - offset, "]"); CRM_LOG_ASSERT(offset > 0); rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search, cib_sync_call | cib_scope_local | cib_xpath); if (rc != pcmk_ok) { goto bail; } crm_log_xml_debug(xml_search, "Match"); if (xml_has_children(xml_search)) { xmlNode *child = NULL; rc = -EINVAL; printf("Multiple attributes match name=%s\n", attr_name); for (child = __xml_first_child(xml_search); child != NULL; child = __xml_next(child)) { printf(" Value: %s \t(id=%s)\n", crm_element_value(child, XML_NVPAIR_ATTR_VALUE), ID(child)); } } else { const char *tmp = crm_element_value(xml_search, attr); if (tmp) { *value = strdup(tmp); } } bail: free(xpath_string); free_xml(xml_search); return rc; } #include "../pengine/pengine.h" static int set_resource_attr(const char *rsc_id, const char *attr_set, const char *attr_id, const char *attr_name, const char *attr_value, bool recursive, cib_t * cib, pe_working_set_t * data_set) { int rc = pcmk_ok; static bool need_init = TRUE; char *local_attr_id = NULL; char *local_attr_set = NULL; xmlNode *xml_top = NULL; xmlNode *xml_obj = NULL; gboolean use_attributes_tag = FALSE; resource_t *rsc = find_rsc_or_clone(rsc_id, data_set); if (rsc == NULL) { return -ENXIO; } if (safe_str_eq(attr_set_type, XML_TAG_ATTR_SETS)) { rc = find_resource_attr(cib, XML_ATTR_ID, rsc_id, XML_TAG_META_SETS, attr_set, attr_id, attr_name, &local_attr_id); if (rc == pcmk_ok) { printf("WARNING: There is already a meta attribute called %s (id=%s)\n", attr_name, local_attr_id); } } rc = find_resource_attr(cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name, &local_attr_id); if (rc == pcmk_ok) { crm_debug("Found a match for name=%s: id=%s", attr_name, local_attr_id); attr_id = local_attr_id; } else if (rc != -ENXIO) { free(local_attr_id); return rc; } else { const char *value = NULL; xmlNode *cib_top = NULL; const char *tag = crm_element_name(rsc->xml); cib->cmds->query(cib, "/cib", &cib_top, cib_sync_call | cib_scope_local | cib_xpath | cib_no_children); value = crm_element_value(cib_top, "ignore_dtd"); if (value != NULL) { use_attributes_tag = TRUE; } else { value = crm_element_value(cib_top, XML_ATTR_VALIDATION); if (value && strstr(value, "-0.6")) { use_attributes_tag = TRUE; } } free_xml(cib_top); if (attr_set == NULL) { local_attr_set = crm_concat(rsc_id, attr_set_type, '-'); attr_set = local_attr_set; } if (attr_id == NULL) { local_attr_id = crm_concat(attr_set, attr_name, '-'); attr_id = local_attr_id; } if (use_attributes_tag && safe_str_eq(tag, XML_CIB_TAG_MASTER)) { tag = "master_slave"; /* use the old name */ } xml_top = create_xml_node(NULL, tag); crm_xml_add(xml_top, XML_ATTR_ID, rsc_id); xml_obj = create_xml_node(xml_top, attr_set_type); crm_xml_add(xml_obj, XML_ATTR_ID, attr_set); if (use_attributes_tag) { xml_obj = create_xml_node(xml_obj, XML_TAG_ATTRS); } } xml_obj = create_xml_node(xml_obj, XML_CIB_TAG_NVPAIR); if (xml_top == NULL) { xml_top = xml_obj; } crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_VALUE, attr_value); crm_log_xml_debug(xml_top, "Update"); rc = cib->cmds->modify(cib, XML_CIB_TAG_RESOURCES, xml_top, cib_options); free_xml(xml_top); free(local_attr_id); free(local_attr_set); if(recursive && safe_str_eq(attr_set_type, XML_TAG_META_SETS)) { GListPtr lpc = NULL; if(need_init) { xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input); need_init = FALSE; unpack_constraints(cib_constraints, data_set); for (lpc = data_set->resources; lpc != NULL; lpc = lpc->next) { resource_t *r = (resource_t *) lpc->data; clear_bit(r->flags, pe_rsc_allocating); } } crm_debug("Looking for dependancies %p", rsc->rsc_cons_lhs); set_bit(rsc->flags, pe_rsc_allocating); for (lpc = rsc->rsc_cons_lhs; lpc != NULL; lpc = lpc->next) { rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data; resource_t *peer = cons->rsc_lh; crm_debug("Checking %s %d", cons->id, cons->score); if (cons->score > 0 && is_not_set(peer->flags, pe_rsc_allocating)) { /* Don't get into colocation loops */ crm_debug("Setting %s=%s for dependant resource %s", attr_name, attr_value, peer->id); set_resource_attr(peer->id, NULL, NULL, attr_name, attr_value, recursive, cib, data_set); } } } return rc; } static int delete_resource_attr(const char *rsc_id, const char *attr_set, const char *attr_id, const char *attr_name, cib_t * cib, pe_working_set_t * data_set) { xmlNode *xml_obj = NULL; int rc = pcmk_ok; char *local_attr_id = NULL; resource_t *rsc = find_rsc_or_clone(rsc_id, data_set); if (rsc == NULL) { return -ENXIO; } rc = find_resource_attr(cib, XML_ATTR_ID, rsc_id, attr_set_type, attr_set, attr_id, attr_name, &local_attr_id); if (rc == -ENXIO) { return pcmk_ok; } else if (rc != pcmk_ok) { return rc; } if (attr_id == NULL) { attr_id = local_attr_id; } xml_obj = create_xml_node(NULL, XML_CIB_TAG_NVPAIR); crm_xml_add(xml_obj, XML_ATTR_ID, attr_id); crm_xml_add(xml_obj, XML_NVPAIR_ATTR_NAME, attr_name); crm_log_xml_debug(xml_obj, "Delete"); CRM_ASSERT(cib); rc = cib->cmds->delete(cib, XML_CIB_TAG_RESOURCES, xml_obj, cib_options); if (rc == pcmk_ok) { printf("Deleted %s option: id=%s%s%s%s%s\n", rsc_id, local_attr_id, attr_set ? " set=" : "", attr_set ? attr_set : "", attr_name ? " name=" : "", attr_name ? attr_name : ""); } free_xml(xml_obj); free(local_attr_id); return rc; } static int dump_resource_prop(const char *rsc, const char *attr, pe_working_set_t * data_set) { const char *value = NULL; resource_t *the_rsc = pe_find_resource(data_set->resources, rsc); if (the_rsc == NULL) { return -ENXIO; } value = crm_element_value(the_rsc->xml, attr); if (value != NULL) { fprintf(stdout, "%s\n", value); return 0; } return -ENXIO; } static int send_lrm_rsc_op(crm_ipc_t * crmd_channel, const char *op, const char *host_uname, const char *rsc_id, gboolean only_failed, pe_working_set_t * data_set) { char *key = NULL; int rc = -ECOMM; xmlNode *cmd = NULL; xmlNode *xml_rsc = NULL; const char *value = NULL; const char *router_node = host_uname; xmlNode *params = NULL; xmlNode *msg_data = NULL; resource_t *rsc = pe_find_resource(data_set->resources, rsc_id); if (rsc == NULL) { CMD_ERR("Resource %s not found\n", rsc_id); return -ENXIO; } else if (rsc->variant != pe_native) { CMD_ERR("We can only process primitive resources, not %s\n", rsc_id); return -EINVAL; } else if (host_uname == NULL) { CMD_ERR("Please supply a hostname with -H\n"); return -EINVAL; } else { node_t *node = pe_find_node(data_set->nodes, host_uname); if (node && is_remote_node(node)) { if (node->details->remote_rsc == NULL || node->details->remote_rsc->running_on == NULL) { CMD_ERR("No lrmd connection detected to remote node %s", host_uname); return -ENXIO; } node = node->details->remote_rsc->running_on->data; router_node = node->details->uname; } } key = generate_transition_key(0, getpid(), 0, "xxxxxxxx-xrsc-opxx-xcrm-resourcexxxx"); msg_data = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP); crm_xml_add(msg_data, XML_ATTR_TRANSITION_KEY, key); free(key); crm_xml_add(msg_data, XML_LRM_ATTR_TARGET, host_uname); if (safe_str_neq(router_node, host_uname)) { crm_xml_add(msg_data, XML_LRM_ATTR_ROUTER_NODE, router_node); } xml_rsc = create_xml_node(msg_data, XML_CIB_TAG_RESOURCE); if (rsc->clone_name) { crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->clone_name); crm_xml_add(xml_rsc, XML_ATTR_ID_LONG, rsc->id); } else { crm_xml_add(xml_rsc, XML_ATTR_ID, rsc->id); } value = crm_element_value(rsc->xml, XML_ATTR_TYPE); crm_xml_add(xml_rsc, XML_ATTR_TYPE, value); if (value == NULL) { CMD_ERR("%s has no type! Aborting...\n", rsc_id); return -ENXIO; } value = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, value); if (value == NULL) { CMD_ERR("%s has no class! Aborting...\n", rsc_id); return -ENXIO; } value = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, value); params = create_xml_node(msg_data, XML_TAG_ATTRS); crm_xml_add(params, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); key = crm_meta_name(XML_LRM_ATTR_INTERVAL); crm_xml_add(params, key, "60000"); /* 1 minute */ free(key); cmd = create_request(op, msg_data, router_node, CRM_SYSTEM_CRMD, crm_system_name, our_pid); /* crm_log_xml_warn(cmd, "send_lrm_rsc_op"); */ free_xml(msg_data); if (crm_ipc_send(crmd_channel, cmd, 0, 0, NULL) > 0) { rc = 0; } else { CMD_ERR("Could not send %s op to the crmd", op); rc = -ENOTCONN; } free_xml(cmd); return rc; } static int delete_lrm_rsc(cib_t *cib_conn, crm_ipc_t * crmd_channel, const char *host_uname, resource_t * rsc, pe_working_set_t * data_set) { int rc = pcmk_ok; node_t *node = NULL; if (rsc == NULL) { return -ENXIO; } else if (rsc->children) { GListPtr lpc = NULL; for (lpc = rsc->children; lpc != NULL; lpc = lpc->next) { resource_t *child = (resource_t *) lpc->data; delete_lrm_rsc(cib_conn, crmd_channel, host_uname, child, data_set); } return pcmk_ok; } else if (host_uname == NULL) { GListPtr lpc = NULL; for (lpc = data_set->nodes; lpc != NULL; lpc = lpc->next) { node = (node_t *) lpc->data; if (node->details->online) { delete_lrm_rsc(cib_conn, crmd_channel, node->details->uname, rsc, data_set); } } return pcmk_ok; } node = pe_find_node(data_set->nodes, host_uname); if (node && node->details->rsc_discovery_enabled) { printf("Cleaning up %s on %s\n", rsc->id, host_uname); rc = send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_DELETE, host_uname, rsc->id, TRUE, data_set); } else { printf("Resource discovery disabled on %s. Unable to delete lrm state.\n", host_uname); } if (rc == pcmk_ok) { char *attr_name = NULL; const char *id = rsc->id; if(node && node->details->remote_rsc == NULL && node->details->rsc_discovery_enabled) { crmd_replies_needed++; } if (rsc->clone_name) { id = rsc->clone_name; } attr_name = crm_concat("fail-count", id, '-'); rc = attrd_update_delegate(NULL, 'D', host_uname, attr_name, NULL, XML_CIB_TAG_STATUS, NULL, NULL, NULL, node ? is_remote_node(node) : FALSE); free(attr_name); } return rc; } static int fail_lrm_rsc(crm_ipc_t * crmd_channel, const char *host_uname, const char *rsc_id, pe_working_set_t * data_set) { crm_warn("Failing: %s", rsc_id); return send_lrm_rsc_op(crmd_channel, CRM_OP_LRM_FAIL, host_uname, rsc_id, FALSE, data_set); } static char * parse_cli_lifetime(const char *input) { char *later_s = NULL; crm_time_t *now = NULL; crm_time_t *later = NULL; crm_time_t *duration = NULL; if (input == NULL) { return NULL; } duration = crm_time_parse_duration(move_lifetime); if (duration == NULL) { CMD_ERR("Invalid duration specified: %s\n", move_lifetime); CMD_ERR("Please refer to" " http://en.wikipedia.org/wiki/ISO_8601#Durations" " for examples of valid durations\n"); return NULL; } now = crm_time_new(NULL); later = crm_time_add(now, duration); crm_time_log(LOG_INFO, "now ", now, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone); crm_time_log(LOG_INFO, "later ", later, crm_time_log_date | crm_time_log_timeofday | crm_time_log_with_timezone); crm_time_log(LOG_INFO, "duration", duration, crm_time_log_date | crm_time_log_timeofday); later_s = crm_time_as_string(later, crm_time_log_date | crm_time_log_timeofday); printf("Migration will take effect until: %s\n", later_s); crm_time_free(duration); crm_time_free(later); crm_time_free(now); return later_s; } static int ban_resource(const char *rsc_id, const char *host, GListPtr allnodes, cib_t * cib_conn) { char *later_s = NULL; int rc = pcmk_ok; char *id = NULL; xmlNode *fragment = NULL; xmlNode *location = NULL; if(host == NULL) { GListPtr n = allnodes; for(; n && rc == pcmk_ok; n = n->next) { node_t *target = n->data; rc = ban_resource(rsc_id, target->details->uname, NULL, cib_conn); } return rc; } later_s = parse_cli_lifetime(move_lifetime); if(move_lifetime && later_s == NULL) { return -EINVAL; } fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); id = g_strdup_printf("cli-ban-%s-on-%s", rsc_id, host); location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(location, XML_ATTR_ID, id); free(id); if (BE_QUIET == FALSE) { CMD_ERR("WARNING: Creating rsc_location constraint '%s'" " with a score of -INFINITY for resource %s" " on %s.\n", ID(location), rsc_id, host); CMD_ERR("\tThis will prevent %s from %s" " on %s until the constraint is removed using" " the 'crm_resource --clear' command or manually" " with cibadmin\n", rsc_id, scope_master?"being promoted":"running", host); CMD_ERR("\tThis will be the case even if %s is" " the last node in the cluster\n", host); CMD_ERR("\tThis message can be disabled with --quiet\n"); } crm_xml_add(location, XML_COLOC_ATTR_SOURCE, rsc_id); if(scope_master) { crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_MASTER_S); } else { crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_STARTED_S); } if (later_s == NULL) { /* Short form */ crm_xml_add(location, XML_CIB_TAG_NODE, host); crm_xml_add(location, XML_RULE_ATTR_SCORE, MINUS_INFINITY_S); } else { xmlNode *rule = create_xml_node(location, XML_TAG_RULE); xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION); id = g_strdup_printf("cli-ban-%s-on-%s-rule", rsc_id, host); crm_xml_add(rule, XML_ATTR_ID, id); free(id); crm_xml_add(rule, XML_RULE_ATTR_SCORE, MINUS_INFINITY_S); crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); id = g_strdup_printf("cli-ban-%s-on-%s-expr", rsc_id, host); crm_xml_add(expr, XML_ATTR_ID, id); free(id); crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname"); crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host); crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); expr = create_xml_node(rule, "date_expression"); id = g_strdup_printf("cli-ban-%s-on-%s-lifetime", rsc_id, host); crm_xml_add(expr, XML_ATTR_ID, id); free(id); crm_xml_add(expr, "operation", "lt"); crm_xml_add(expr, "end", later_s); } crm_log_xml_notice(fragment, "Modify"); rc = cib_conn->cmds->update(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options); free_xml(fragment); free(later_s); return rc; } static int prefer_resource(const char *rsc_id, const char *host, cib_t * cib_conn) { char *later_s = parse_cli_lifetime(move_lifetime); int rc = pcmk_ok; char *id = NULL; xmlNode *location = NULL; xmlNode *fragment = NULL; if(move_lifetime && later_s == NULL) { return -EINVAL; } if(cib_conn == NULL) { free(later_s); return -ENOTCONN; } fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); id = g_strdup_printf("cli-prefer-%s", rsc_id); location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(location, XML_ATTR_ID, id); free(id); crm_xml_add(location, XML_COLOC_ATTR_SOURCE, rsc_id); if(scope_master) { crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_MASTER_S); } else { crm_xml_add(location, XML_RULE_ATTR_ROLE, RSC_ROLE_STARTED_S); } if (later_s == NULL) { /* Short form */ crm_xml_add(location, XML_CIB_TAG_NODE, host); crm_xml_add(location, XML_RULE_ATTR_SCORE, INFINITY_S); } else { xmlNode *rule = create_xml_node(location, XML_TAG_RULE); xmlNode *expr = create_xml_node(rule, XML_TAG_EXPRESSION); id = crm_concat("cli-prefer-rule", rsc_id, '-'); crm_xml_add(rule, XML_ATTR_ID, id); free(id); crm_xml_add(rule, XML_RULE_ATTR_SCORE, INFINITY_S); crm_xml_add(rule, XML_RULE_ATTR_BOOLEAN_OP, "and"); id = crm_concat("cli-prefer-expr", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); free(id); crm_xml_add(expr, XML_EXPR_ATTR_ATTRIBUTE, "#uname"); crm_xml_add(expr, XML_EXPR_ATTR_OPERATION, "eq"); crm_xml_add(expr, XML_EXPR_ATTR_VALUE, host); crm_xml_add(expr, XML_EXPR_ATTR_TYPE, "string"); expr = create_xml_node(rule, "date_expression"); id = crm_concat("cli-prefer-lifetime-end", rsc_id, '-'); crm_xml_add(expr, XML_ATTR_ID, id); free(id); crm_xml_add(expr, "operation", "lt"); crm_xml_add(expr, "end", later_s); } crm_log_xml_info(fragment, "Modify"); rc = cib_conn->cmds->update(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options); free_xml(fragment); free(later_s); return rc; } static int clear_resource(const char *rsc_id, const char *host, GListPtr allnodes, cib_t * cib_conn) { char *id = NULL; int rc = pcmk_ok; xmlNode *fragment = NULL; xmlNode *location = NULL; if(cib_conn == NULL) { return -ENOTCONN; } fragment = create_xml_node(NULL, XML_CIB_TAG_CONSTRAINTS); if(host) { id = g_strdup_printf("cli-ban-%s-on-%s", rsc_id, host); location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(location, XML_ATTR_ID, id); free(id); } else { GListPtr n = allnodes; for(; n; n = n->next) { node_t *target = n->data; id = g_strdup_printf("cli-ban-%s-on-%s", rsc_id, target->details->uname); location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(location, XML_ATTR_ID, id); free(id); } } id = g_strdup_printf("cli-prefer-%s", rsc_id); location = create_xml_node(fragment, XML_CONS_TAG_RSC_LOCATION); crm_xml_add(location, XML_ATTR_ID, id); if(host && do_force == FALSE) { crm_xml_add(location, XML_CIB_TAG_NODE, host); } free(id); crm_log_xml_info(fragment, "Delete"); rc = cib_conn->cmds->delete(cib_conn, XML_CIB_TAG_CONSTRAINTS, fragment, cib_options); if (rc == -ENXIO) { rc = pcmk_ok; } else if (rc != pcmk_ok) { goto bail; } bail: free_xml(fragment); return rc; } static int list_resource_operations(const char *rsc_id, const char *host_uname, gboolean active, pe_working_set_t * data_set) { resource_t *rsc = NULL; int opts = pe_print_printf | pe_print_rsconly | pe_print_suppres_nl; GListPtr ops = find_operations(rsc_id, host_uname, active, data_set); GListPtr lpc = NULL; if (print_pending) { opts |= pe_print_pending; } for (lpc = ops; lpc != NULL; lpc = lpc->next) { xmlNode *xml_op = (xmlNode *) lpc->data; const char *op_rsc = crm_element_value(xml_op, "resource"); const char *last = crm_element_value(xml_op, XML_RSC_OP_LAST_CHANGE); const char *status_s = crm_element_value(xml_op, XML_LRM_ATTR_OPSTATUS); const char *op_key = crm_element_value(xml_op, XML_LRM_ATTR_TASK_KEY); int status = crm_parse_int(status_s, "0"); rsc = pe_find_resource(data_set->resources, op_rsc); if(rsc) { rsc->fns->print(rsc, "", opts, stdout); } else { fprintf(stdout, "Unknown resource %s", op_rsc); } fprintf(stdout, ": %s (node=%s, call=%s, rc=%s", op_key ? op_key : ID(xml_op), crm_element_value(xml_op, XML_ATTR_UNAME), crm_element_value(xml_op, XML_LRM_ATTR_CALLID), crm_element_value(xml_op, XML_LRM_ATTR_RC)); if (last) { time_t run_at = crm_parse_int(last, "0"); fprintf(stdout, ", last-rc-change=%s, exec=%sms", crm_strip_trailing_newline(ctime(&run_at)), crm_element_value(xml_op, XML_RSC_OP_T_EXEC)); } fprintf(stdout, "): %s\n", services_lrm_status_str(status)); } return pcmk_ok; } static void show_location(resource_t * rsc, const char *prefix) { GListPtr lpc = NULL; GListPtr list = rsc->rsc_location; int offset = 0; if (prefix) { offset = strlen(prefix) - 2; } for (lpc = list; lpc != NULL; lpc = lpc->next) { rsc_to_node_t *cons = (rsc_to_node_t *) lpc->data; GListPtr lpc2 = NULL; for (lpc2 = cons->node_list_rh; lpc2 != NULL; lpc2 = lpc2->next) { node_t *node = (node_t *) lpc2->data; char *score = score2char(node->weight); fprintf(stdout, "%s: Node %-*s (score=%s, id=%s)\n", prefix ? prefix : " ", 71 - offset, node->details->uname, score, cons->id); free(score); } } } static void show_colocation(resource_t * rsc, gboolean dependants, gboolean recursive, int offset) { char *prefix = NULL; GListPtr lpc = NULL; GListPtr list = rsc->rsc_cons; prefix = calloc(1, (offset * 4) + 1); memset(prefix, ' ', offset * 4); if (dependants) { list = rsc->rsc_cons_lhs; } if (is_set(rsc->flags, pe_rsc_allocating)) { /* Break colocation loops */ printf("loop %s\n", rsc->id); free(prefix); return; } set_bit(rsc->flags, pe_rsc_allocating); for (lpc = list; lpc != NULL; lpc = lpc->next) { rsc_colocation_t *cons = (rsc_colocation_t *) lpc->data; char *score = NULL; resource_t *peer = cons->rsc_rh; if (dependants) { peer = cons->rsc_lh; } if (is_set(peer->flags, pe_rsc_allocating)) { if (dependants == FALSE) { fprintf(stdout, "%s%-*s (id=%s - loop)\n", prefix, 80 - (4 * offset), peer->id, cons->id); } continue; } if (dependants && recursive) { show_colocation(peer, dependants, recursive, offset + 1); } score = score2char(cons->score); if (cons->role_rh > RSC_ROLE_STARTED) { fprintf(stdout, "%s%-*s (score=%s, %s role=%s, id=%s)\n", prefix, 80 - (4 * offset), peer->id, score, dependants ? "needs" : "with", role2text(cons->role_rh), cons->id); } else { fprintf(stdout, "%s%-*s (score=%s, id=%s)\n", prefix, 80 - (4 * offset), peer->id, score, cons->id); } show_location(peer, prefix); free(score); if (!dependants && recursive) { show_colocation(peer, dependants, recursive, offset + 1); } } free(prefix); } static GHashTable * generate_resource_params(resource_t * rsc, pe_working_set_t * data_set) { GHashTable *params = NULL; GHashTable *meta = NULL; GHashTable *combined = NULL; GHashTableIter iter; if (!rsc) { crm_err("Resource does not exist in config"); return NULL; } params = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); meta = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); combined = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); get_rsc_attributes(params, rsc, NULL /* TODO: Pass in local node */ , data_set); get_meta_attributes(meta, rsc, NULL /* TODO: Pass in local node */ , data_set); if (params) { char *key = NULL; char *value = NULL; g_hash_table_iter_init(&iter, params); while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { g_hash_table_insert(combined, strdup(key), strdup(value)); } g_hash_table_destroy(params); } if (meta) { char *key = NULL; char *value = NULL; g_hash_table_iter_init(&iter, meta); while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { char *crm_name = crm_meta_name(key); g_hash_table_insert(combined, crm_name, strdup(value)); } g_hash_table_destroy(meta); } return combined; } +static bool resource_is_running_on(resource_t *rsc, const char *host) +{ + bool found = TRUE; + GListPtr hIter = NULL; + GListPtr hosts = NULL; + + if(rsc == NULL) { + return FALSE; + } + + rsc->fns->location(rsc, &hosts, TRUE); + for (hIter = hosts; host != NULL && hIter != NULL; hIter = hIter->next) { + pe_node_t *node = (pe_node_t *) hIter->data; + + if(strcmp(host, node->details->uname) == 0) { + crm_trace("Resource %s is running on %s\n", rsc->id, host); + goto done; + } else if(strcmp(host, node->details->id) == 0) { + crm_trace("Resource %s is running on %s\n", rsc->id, host); + goto done; + } + } + + if(host != NULL && hosts != NULL) { + crm_trace("Resource %s is not running on: %s\n", rsc->id, host); + found = FALSE; + + } else if(host == NULL && hosts == NULL) { + crm_trace("Resource %s is not running\n", rsc->id); + found = FALSE; + } + + done: + + g_list_free(hosts); + return found; +} + +static GList *get_active_resources(const char *host, pe_working_set_t *data_set) +{ + GList *rIter = NULL; + GList *active = NULL; + + for (rIter = data_set->resources; rIter != NULL; rIter = rIter->next) { + resource_t *rsc = (resource_t *) rIter->data; + + if(resource_is_running_on(rsc, host)) { + active = g_list_append(active, strdup(rsc->id)); + } + } + + return active; +} + +static GList *subtract_lists(GList *from, GList *items) +{ + GList *item = NULL; + GList *result = g_list_copy(from); + + for (item = items; item != NULL; item = item->next) { + GList *candidate = NULL; + for (candidate = from; candidate != NULL; candidate = candidate->next) { + crm_info("Comparing %s with %s", candidate->data, item->data); + if(strcmp(candidate->data, item->data) == 0) { + result = g_list_remove(result, candidate->data); + break; + } + } + } + + return result; +} + +static void dump_list(GList *items, const char *tag) +{ + int lpc = 0; + GList *item = NULL; + + for (item = items; item != NULL; item = item->next) { + crm_trace("%s[%d]: %s", tag, lpc, item->data); + lpc++; + } +} + +static void display_list(GList *items, const char *tag) +{ + GList *item = NULL; + + for (item = items; item != NULL; item = item->next) { + fprintf(stdout, "%s%s\n", tag, (const char *)item->data); + } +} + +static int +update_dataset(cib_t *cib, pe_working_set_t * data_set, bool simulate) +{ + xmlNode *cib_xml_copy = NULL; + int rc = cib->cmds->query(cib, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call); + + if(rc != pcmk_ok) { + fprintf(stdout, "Could not obtain the current CIB: %s (%d)\n", pcmk_strerror(rc), rc); + return crm_exit(rc); + + } else if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) { + fprintf(stderr, "Could not upgrade the current CIB\n"); + return -ENOKEY; + } + + set_working_set_defaults(data_set); + data_set->input = cib_xml_copy; + data_set->now = crm_time_new(NULL); + + if(simulate) { + char *pid = crm_itoa(getpid()); + cib_t *shadow_cib = cib_shadow_new(pid); + char *shadow_file = get_shadow_file(pid); + + if (shadow_cib == NULL) { + fprintf(stderr, "Could not create shadow cib: '%s'\n", pid); + crm_exit(-ENXIO); + } + + rc = write_xml_file(cib_xml_copy, shadow_file, FALSE); + + if (rc < 0) { + fprintf(stderr, "Could not populate shadow cib: %s (%d)\n", pcmk_strerror(rc), rc); + free_xml(cib_xml_copy); + return rc; + } + + rc = shadow_cib->cmds->signon(shadow_cib, crm_system_name, cib_command); + if(rc != pcmk_ok) { + fprintf(stderr, "Could not connect to shadow cib: %s (%d)\n", pcmk_strerror(rc), rc); + free_xml(cib_xml_copy); + return rc; + } + + do_calculations(data_set, cib_xml_copy, NULL); + run_simulation(data_set, shadow_cib, NULL, TRUE); + rc = update_dataset(shadow_cib, data_set, FALSE); + + cib_delete(shadow_cib); + /* unlink(shadow_file); */ + free(shadow_file); + + } else { + cluster_status(data_set); + } + + return rc; +} + +static int +max_delay_in(pe_working_set_t * data_set, GList *resources) +{ + return 30; +} + +static int +resource_restart(resource_t * rsc, const char *host, int timeout_ms, cib_t * cib) +{ + int rc = pcmk_ok; + + GList *list_delta = NULL; + GList *target_active = NULL; + GList *current_active = NULL; + GList *restart_target_active = NULL; + + pe_working_set_t data_set; + + if(resource_is_running_on(rsc, host) == FALSE) { + return -ENXIO; + } + + /* +grab full cib +use constraints to determine ordered list of affected resources +determine resource state of list +disable or ban +unless --no-deps, listen for cib updates and watch for resources to get stopped +without --wait, calculate the stop timeout for each step and wait for that +if we hit --wait or the service timeout, re-enable or un-ban, report failure and indicate which resources we couldn't take down +if everything stopped, re-enable or un-ban +unless --no-deps, listen for cib updates and watch for resources to get started +without --wait, calculate the start timeout for each step and wait for that +if we hit --wait or the service timeout, report (different) failure and indicate which resources we couldn't bring back up +report success + */ + + + set_working_set_defaults(&data_set); + rc = update_dataset(cib, &data_set, FALSE); + if(rc != pcmk_ok) { + fprintf(stdout, "Could not get new resource list: %s (%d)\n", pcmk_strerror(rc), rc); + return rc; + } + + restart_target_active = get_active_resources(host, &data_set); + current_active = get_active_resources(host, &data_set); + + dump_list(current_active, "Origin"); + + rc = set_resource_attr(rsc->id, NULL, NULL, XML_RSC_ATTR_TARGET_ROLE, RSC_STOPPED, FALSE, cib, &data_set); + if(rc != pcmk_ok) { + fprintf(stdout, "Could not set target-role for %s: %s (%d)\n", rsc->id, pcmk_strerror(rc), rc); + return crm_exit(rc); + } + + rc = update_dataset(cib, &data_set, TRUE); + if(rc != pcmk_ok) { + fprintf(stdout, "Could not get new resource list: %s (%d)\n", pcmk_strerror(rc), rc); + return rc; + } + target_active = get_active_resources(host, &data_set); + dump_list(target_active, "Target"); + + list_delta = subtract_lists(current_active, target_active); + fprintf(stdout, "Waiting for %d resources to stop:\n", g_list_length(list_delta)); + display_list(list_delta, " * "); + + while(g_list_length(list_delta) > 0) { + int before = g_list_length(list_delta); + int max_delay = max_delay_in(&data_set, list_delta); + + sleep(max_delay); + rc = update_dataset(cib, &data_set, FALSE); + if(rc != pcmk_ok) { + fprintf(stdout, "Could not get new resource list: %s (%d)\n", pcmk_strerror(rc), rc); + return rc; + } + current_active = get_active_resources(host, &data_set); + list_delta = subtract_lists(current_active, target_active); + dump_list(current_active, "Current"); + dump_list(list_delta, "Delta"); + + if(before == g_list_length(list_delta)) { + /* aborted during stop phase, print the contents of list_delta */ + fprintf(stdout, "Could not complete shutdown of %s, %d resources remaining\n", rsc->id, g_list_length(list_delta)); + display_list(list_delta, " * "); + return -ETIME; + } + + } while(g_list_length(list_delta) > 0); + + rc = delete_resource_attr(rsc->id, NULL, NULL, XML_RSC_ATTR_TARGET_ROLE, cib, &data_set); + target_active = restart_target_active; + + list_delta = subtract_lists(target_active, current_active); + fprintf(stdout, "Waiting for %d resources to start again:\n", g_list_length(list_delta)); + display_list(list_delta, " * "); + + while(g_list_length(list_delta) > 0) { + int before = g_list_length(list_delta); + int max_delay = max_delay_in(&data_set, list_delta); + + sleep(max_delay); + rc = update_dataset(cib, &data_set, FALSE); + if(rc != pcmk_ok) { + fprintf(stdout, "Could not get new resource list: %s (%d)\n", pcmk_strerror(rc), rc); + return rc; + } + + current_active = get_active_resources(host, &data_set); + list_delta = subtract_lists(target_active, current_active); + dump_list(current_active, "Current"); + dump_list(list_delta, "Delta"); + + if(before == g_list_length(list_delta)) { + /* aborted during start phase, print the contents of list_delta */ + fprintf(stdout, "Could not complete restart of %s, %d resources remaining\n", rsc->id, g_list_length(list_delta)); + display_list(list_delta, " * "); + return -ETIME; + } + + } while(g_list_length(list_delta) > 0); + + return pcmk_ok; +} /* *INDENT-OFF* */ static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\t\tThis text"}, {"version", 0, 0, '$', "\t\tVersion information" }, {"verbose", 0, 0, 'V', "\t\tIncrease debug output"}, {"quiet", 0, 0, 'Q', "\t\tPrint only the value on stdout\n"}, {"resource", 1, 0, 'r', "\tResource ID" }, {"-spacer-",1, 0, '-', "\nQueries:"}, {"list", 0, 0, 'L', "\t\tList all cluster resources"}, {"list-raw", 0, 0, 'l', "\tList the IDs of all instantiated resources (no groups/clones/...)"}, {"list-cts", 0, 0, 'c', NULL, 1}, {"list-operations", 0, 0, 'O', "\tList active resource operations. Optionally filtered by resource (-r) and/or node (-N)"}, {"list-all-operations", 0, 0, 'o', "List all resource operations. Optionally filtered by resource (-r) and/or node (-N)"}, {"pending", 0, 0, 'j', "\t\tDisplay pending state if 'record-pending' is enabled\n"}, {"list-standards", 0, 0, 0, "\tList supported standards"}, {"list-ocf-providers", 0, 0, 0, "List all available OCF providers"}, {"list-agents", 1, 0, 0, "List all agents available for the named standard and/or provider."}, {"list-ocf-alternatives", 1, 0, 0, "List all available providers for the named OCF agent\n"}, {"show-metadata", 1, 0, 0, "Show the metadata for the named class:provider:agent"}, {"query-xml", 0, 0, 'q', "\tQuery the definition of a resource (template expanded)"}, {"query-xml-raw", 0, 0, 'w', "\tQuery the definition of a resource (raw xml)"}, {"locate", 0, 0, 'W', "\t\tDisplay the current location(s) of a resource"}, {"stack", 0, 0, 'A', "\t\tDisplay the prerequisites and dependents of a resource"}, {"constraints",0, 0, 'a', "\tDisplay the (co)location constraints that apply to a resource"}, {"-spacer-", 1, 0, '-', "\nCommands:"}, {"cleanup", 0, 0, 'C', "\t\tDelete the resource history and re-check the current state. Optional: --resource"}, {"set-parameter", 1, 0, 'p', "Set the named parameter for a resource. See also -m, --meta"}, {"get-parameter", 1, 0, 'g', "Display the named parameter for a resource. See also -m, --meta"}, {"delete-parameter",1, 0, 'd', "Delete the named parameter for a resource. See also -m, --meta"}, {"get-property", 1, 0, 'G', "Display the 'class', 'type' or 'provider' of a resource", 1}, {"set-property", 1, 0, 'S', "(Advanced) Set the class, type or provider of a resource", 1}, {"-spacer-", 1, 0, '-', "\nResource location:"}, { "move", 0, 0, 'M', "\t\tMove a resource from its current location to the named destination.\n " "\t\t\t\tRequires: --host. Optional: --lifetime, --master\n\n" "\t\t\t\tNOTE: This may prevent the resource from running on the previous location node until the implicit constraints expire or are removed with --unban\n" }, { "ban", 0, 0, 'B', "\t\tPrevent the named resource from running on the named --host. \n" "\t\t\t\tRequires: --resource. Optional: --host, --lifetime, --master\n\n" "\t\t\t\tIf --host is not specified, it defaults to:\n" "\t\t\t\t * the curent location for primitives and groups, or\n\n" "\t\t\t\t * the curent location of the master for m/s resources with master-max=1\n\n" "\t\t\t\tAll other situations result in an error as there is no sane default.\n\n" "\t\t\t\tNOTE: This will prevent the resource from running on this node until the constraint expires or is removed with --clear\n" }, { "clear", 0, 0, 'U', "\t\tRemove all constraints created by the --ban and/or --move commands. \n" "\t\t\t\tRequires: --resource. Optional: --host, --master\n\n" "\t\t\t\tIf --host is not specified, all constraints created by --ban and --move will be removed for the named resource.\n" }, {"lifetime", 1, 0, 'u', "\tLifespan of constraints created by the --ban and --move commands"}, { "master", 0, 0, 0, "\t\tLimit the scope of the --ban, --move and --clear commands to the Master role.\n" "\t\t\t\tFor --ban and --move, the previous master can still remain active in the Slave role." }, {"-spacer-", 1, 0, '-', "\nAdvanced Commands:"}, {"delete", 0, 0, 'D', "\t\t(Advanced) Delete a resource from the CIB"}, {"fail", 0, 0, 'F', "\t\t(Advanced) Tell the cluster this resource has failed"}, + {"restart", 0, 0, 0, NULL, 1}, {"force-stop", 0, 0, 0, "\t(Advanced) Bypass the cluster and stop a resource on the local node. Additional detail with -V"}, {"force-start",0, 0, 0, "\t(Advanced) Bypass the cluster and start a resource on the local node. Additional detail with -V"}, {"force-check",0, 0, 0, "\t(Advanced) Bypass the cluster and check the state of a resource on the local node. Additional detail with -V\n"}, {"-spacer-", 1, 0, '-', "\nAdditional Options:"}, {"node", 1, 0, 'N', "\tHost uname"}, {"recursive", 0, 0, 0, "\tFollow colocation chains when using --set-parameter"}, {"resource-type", 1, 0, 't', "Resource type (primitive, clone, group, ...)"}, {"parameter-value", 1, 0, 'v', "Value to use with -p or -S"}, {"meta", 0, 0, 'm', "\t\tModify a resource's configuration option rather than one which is passed to the resource agent script. For use with -p, -g, -d"}, {"utilization", 0, 0, 'z', "\tModify a resource's utilization attribute. For use with -p, -g, -d"}, {"set-name", 1, 0, 's', "\t(Advanced) ID of the instance_attributes object to change"}, {"nvpair", 1, 0, 'i', "\t(Advanced) ID of the nvpair object to change/delete"}, {"force", 0, 0, 'f', "\n" /* Is this actually true anymore? "\t\tForce the resource to move by creating a rule for the current location and a score of -INFINITY" "\n\t\tThis should be used if the resource's stickiness and constraint scores total more than INFINITY (Currently 100,000)" "\n\t\tNOTE: This will prevent the resource from running on this node until the constraint is removed with -U or the --lifetime duration expires\n"*/ }, {"xml-file", 1, 0, 'x', NULL, 1},\ /* legacy options */ {"host-uname", 1, 0, 'H', NULL, 1}, {"migrate", 0, 0, 'M', NULL, 1}, {"un-migrate", 0, 0, 'U', NULL, 1}, {"un-move", 0, 0, 'U', NULL, 1}, {"refresh", 0, 0, 'R', NULL, 1}, {"reprobe", 0, 0, 'P', NULL, 1}, {"-spacer-", 1, 0, '-', "\nExamples:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "List the configured resources:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --list", pcmk_option_example}, {"-spacer-", 1, 0, '-', "List the available OCF agents:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --list-agents ocf", pcmk_option_example}, {"-spacer-", 1, 0, '-', "List the available OCF agents from the linux-ha project:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --list-agents ocf:heartbeat", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Display the current location of 'myResource':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --locate", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Move 'myResource' to another machine:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --move", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Move 'myResource' to a specific machine:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --move --node altNode", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Allow (but not force) 'myResource' to move back to its original location:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --un-move", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Tell the cluster that 'myResource' failed:", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --fail", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Stop a 'myResource' (and anything that depends on it):", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --set-parameter target-role --meta --parameter-value Stopped", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Tell the cluster not to manage 'myResource':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "The cluster will not attempt to start or stop the resource under any circumstances."}, {"-spacer-", 1, 0, '-', "Useful when performing maintenance tasks on a resource.", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --set-parameter is-managed --meta --parameter-value false", pcmk_option_example}, {"-spacer-", 1, 0, '-', "Erase the operation history of 'myResource' on 'aNode':", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', "The cluster will 'forget' the existing resource state (including any errors) and attempt to recover the resource."}, {"-spacer-", 1, 0, '-', "Useful when a resource had failed permanently and has been repaired by an administrator.", pcmk_option_paragraph}, {"-spacer-", 1, 0, '-', " crm_resource --resource myResource --cleanup --node aNode", pcmk_option_example}, {0, 0, 0, 0} }; /* *INDENT-ON* */ int main(int argc, char **argv) { const char *longname = NULL; pe_working_set_t data_set; xmlNode *cib_xml_copy = NULL; cib_t *cib_conn = NULL; bool do_trace = FALSE; bool recursive = FALSE; int rc = pcmk_ok; int option_index = 0; int argerr = 0; int flag; crm_log_cli_init("crm_resource"); crm_set_options(NULL, "(query|command) [options]", long_options, "Perform tasks related to cluster resources.\nAllows resources to be queried (definition and location), modified, and moved around the cluster.\n"); if (argc < 2) { crm_help('?', EX_USAGE); } while (1) { flag = crm_get_option_long(argc, argv, &option_index, &longname); if (flag == -1) break; switch (flag) { case 0: if (safe_str_eq("master", longname)) { scope_master = TRUE; } else if(safe_str_eq(longname, "recursive")) { recursive = TRUE; } else if (safe_str_eq("force-stop", longname) + || safe_str_eq("restart", longname) || safe_str_eq("force-start", longname) || safe_str_eq("force-check", longname)) { rsc_cmd = flag; rsc_long_cmd = longname; } else if (safe_str_eq("list-ocf-providers", longname) || safe_str_eq("list-ocf-alternatives", longname) || safe_str_eq("list-standards", longname)) { const char *text = NULL; lrmd_list_t *list = NULL; lrmd_list_t *iter = NULL; lrmd_t *lrmd_conn = lrmd_api_new(); if (safe_str_eq("list-ocf-providers", longname) || safe_str_eq("list-ocf-alternatives", longname)) { rc = lrmd_conn->cmds->list_ocf_providers(lrmd_conn, optarg, &list); text = "OCF providers"; } else if (safe_str_eq("list-standards", longname)) { rc = lrmd_conn->cmds->list_standards(lrmd_conn, &list); text = "standards"; } if (rc > 0) { rc = 0; for (iter = list; iter != NULL; iter = iter->next) { rc++; printf("%s\n", iter->val); } lrmd_list_freeall(list); } else if (optarg) { fprintf(stderr, "No %s found for %s\n", text, optarg); } else { fprintf(stderr, "No %s found\n", text); } lrmd_api_delete(lrmd_conn); return crm_exit(rc); } else if (safe_str_eq("show-metadata", longname)) { char standard[512]; char provider[512]; char type[512]; char *metadata = NULL; lrmd_t *lrmd_conn = lrmd_api_new(); rc = sscanf(optarg, "%[^:]:%[^:]:%s", standard, provider, type); if (rc == 3) { rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard, provider, type, &metadata, 0); } else if (rc == 2) { rc = lrmd_conn->cmds->get_metadata(lrmd_conn, standard, NULL, provider, &metadata, 0); } else if (rc < 2) { fprintf(stderr, "Please specify standard:type or standard:provider:type, not %s\n", optarg); rc = -EINVAL; } if (metadata) { printf("%s\n", metadata); } else { fprintf(stderr, "Metadata query for %s failed: %d\n", optarg, rc); } lrmd_api_delete(lrmd_conn); return crm_exit(rc); } else if (safe_str_eq("list-agents", longname)) { lrmd_list_t *list = NULL; lrmd_list_t *iter = NULL; char standard[512]; char provider[512]; lrmd_t *lrmd_conn = lrmd_api_new(); rc = sscanf(optarg, "%[^:]:%s", standard, provider); if (rc == 1) { rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, optarg, NULL); provider[0] = '*'; provider[1] = 0; } else if (rc == 2) { rc = lrmd_conn->cmds->list_agents(lrmd_conn, &list, standard, provider); } if (rc > 0) { rc = 0; for (iter = list; iter != NULL; iter = iter->next) { printf("%s\n", iter->val); rc++; } lrmd_list_freeall(list); rc = 0; } else { fprintf(stderr, "No agents found for standard=%s, provider=%s\n", standard, provider); rc = -1; } lrmd_api_delete(lrmd_conn); return crm_exit(rc); } else { crm_err("Unhandled long option: %s", longname); } break; case 'V': do_trace = TRUE; crm_bump_log_level(argc, argv); break; case '$': case '?': crm_help(flag, EX_OK); break; case 'x': xml_file = strdup(optarg); break; case 'Q': BE_QUIET = TRUE; break; case 'm': attr_set_type = XML_TAG_META_SETS; break; case 'z': attr_set_type = XML_TAG_UTILIZATION; break; case 'u': move_lifetime = strdup(optarg); break; case 'f': do_force = TRUE; break; case 'i': prop_id = optarg; break; case 's': prop_set = optarg; break; case 'r': rsc_id = optarg; break; case 'v': prop_value = optarg; break; case 't': rsc_type = optarg; break; case 'C': case 'R': case 'P': rsc_cmd = 'C'; break; case 'L': case 'c': case 'l': case 'q': case 'w': case 'D': case 'F': case 'W': case 'M': case 'U': case 'B': case 'O': case 'o': case 'A': case 'a': rsc_cmd = flag; break; case 'j': print_pending = TRUE; break; case 'p': case 'g': case 'd': case 'S': case 'G': prop_name = optarg; rsc_cmd = flag; break; case 'h': case 'H': case 'N': crm_trace("Option %c => %s", flag, optarg); host_uname = optarg; break; default: CMD_ERR("Argument code 0%o (%c) is not (?yet?) supported\n", flag, flag); ++argerr; break; } } if (optind < argc && argv[optind] != NULL) { CMD_ERR("non-option ARGV-elements: "); while (optind < argc && argv[optind] != NULL) { CMD_ERR("%s ", argv[optind++]); ++argerr; } CMD_ERR("\n"); } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', EX_USAGE); } our_pid = calloc(1, 11); if (our_pid != NULL) { snprintf(our_pid, 10, "%d", getpid()); our_pid[10] = '\0'; } if (do_force) { crm_debug("Forcing..."); cib_options |= cib_quorum_override; } set_working_set_defaults(&data_set); if (rsc_cmd != 'P' || rsc_id) { resource_t *rsc = NULL; cib_conn = cib_new(); rc = cib_conn->cmds->signon(cib_conn, crm_system_name, cib_command); if (rc != pcmk_ok) { CMD_ERR("Error signing on to the CIB service: %s\n", pcmk_strerror(rc)); return crm_exit(rc); } if (xml_file != NULL) { cib_xml_copy = filename2xml(xml_file); } else { rc = cib_conn->cmds->query(cib_conn, NULL, &cib_xml_copy, cib_scope_local | cib_sync_call); } if(rc != pcmk_ok) { goto bail; } else if (cli_config_update(&cib_xml_copy, NULL, FALSE) == FALSE) { rc = -ENOKEY; goto bail; } data_set.input = cib_xml_copy; data_set.now = crm_time_new(NULL); cluster_status(&data_set); if (rsc_id) { rsc = find_rsc_or_clone(rsc_id, &data_set); } if (rsc == NULL && rsc_cmd != 'C') { rc = -ENXIO; } } if (rsc_cmd == 'R' || rsc_cmd == 'C' || rsc_cmd == 'F' || rsc_cmd == 'P') { xmlNode *xml = NULL; mainloop_io_t *source = mainloop_add_ipc_client(CRM_SYSTEM_CRMD, G_PRIORITY_DEFAULT, 0, NULL, &crm_callbacks); crmd_channel = mainloop_get_ipc_client(source); if (crmd_channel == NULL) { CMD_ERR("Error signing on to the CRMd service\n"); rc = -ENOTCONN; goto bail; } xml = create_hello_message(our_pid, crm_system_name, "0", "1"); crm_ipc_send(crmd_channel, xml, 0, 0, NULL); free_xml(xml); } if (rsc_cmd == 'L') { rc = pcmk_ok; do_find_resource_list(&data_set, FALSE); } else if (rsc_cmd == 'l') { int found = 0; GListPtr lpc = NULL; rc = pcmk_ok; for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *rsc = (resource_t *) lpc->data; found++; print_raw_rsc(rsc); } if (found == 0) { printf("NO resources configured\n"); rc = -ENXIO; goto bail; } + } else if (rsc_cmd == 0 && rsc_long_cmd && safe_str_eq(rsc_long_cmd, "restart")) { + resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); + + rc = resource_restart(rsc, host_uname, 0, cib_conn); + } else if (rsc_cmd == 0 && rsc_long_cmd) { svc_action_t *op = NULL; const char *rtype = NULL; const char *rprov = NULL; const char *rclass = NULL; const char *action = NULL; GHashTable *params = NULL; resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); if (rsc == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } if (safe_str_eq(rsc_long_cmd, "force-stop")) { action = "stop"; } else if (safe_str_eq(rsc_long_cmd, "force-start")) { action = "start"; if(rsc->variant >= pe_clone) { rc = do_find_resource(rsc_id, NULL, &data_set); if(rc > 0 && do_force == FALSE) { CMD_ERR("It is not safe to start %s here: the cluster claims it is already active", rsc_id); CMD_ERR("Try setting target-role=stopped first or specifying --force"); crm_exit(EPERM); } } } else if (safe_str_eq(rsc_long_cmd, "force-check")) { action = "monitor"; } + if(rsc->variant == pe_clone || rsc->variant == pe_master) { + /* Grab the first child resource in the hope its not a group */ + rsc = rsc->children->data; + } + + if(rsc->variant == pe_group) { + CMD_ERR("Sorry, --%s doesn't support group resources\n", rsc_long_cmd); + crm_exit(EOPNOTSUPP); + } + rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); if(safe_str_eq(rclass, "stonith")){ CMD_ERR("Sorry, --%s doesn't support %s resources yet\n", rsc_long_cmd, rclass); crm_exit(EOPNOTSUPP); } params = generate_resource_params(rsc, &data_set); op = resources_action_create(rsc->id, rclass, rprov, rtype, action, 0, -1, params); if(do_trace) { setenv("OCF_TRACE_RA", "1", 1); } if(op == NULL) { /* Re-run but with stderr enabled so we can display a sane error message */ crm_enable_stderr(TRUE); resources_action_create(rsc->id, rclass, rprov, rtype, action, 0, -1, params); return crm_exit(EINVAL); } else if (services_action_sync(op)) { int more, lpc, last; char *local_copy = NULL; if (op->status == PCMK_LRM_OP_DONE) { printf("Operation %s for %s (%s:%s:%s) returned %d\n", action, rsc->id, rclass, rprov ? rprov : "", rtype, op->rc); } else { printf("Operation %s for %s (%s:%s:%s) failed: %d\n", action, rsc->id, rclass, rprov ? rprov : "", rtype, op->status); } if (op->stdout_data) { local_copy = strdup(op->stdout_data); more = strlen(local_copy); last = 0; for (lpc = 0; lpc < more; lpc++) { if (local_copy[lpc] == '\n' || local_copy[lpc] == 0) { local_copy[lpc] = 0; printf(" > stdout: %s\n", local_copy + last); last = lpc + 1; } } free(local_copy); } if (op->stderr_data) { local_copy = strdup(op->stderr_data); more = strlen(local_copy); last = 0; for (lpc = 0; lpc < more; lpc++) { if (local_copy[lpc] == '\n' || local_copy[lpc] == 0) { local_copy[lpc] = 0; printf(" > stderr: %s\n", local_copy + last); last = lpc + 1; } } free(local_copy); } } rc = op->rc; services_action_free(op); return crm_exit(rc); } else if (rsc_cmd == 'A' || rsc_cmd == 'a') { GListPtr lpc = NULL; resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set.input); if (rsc == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } unpack_constraints(cib_constraints, &data_set); for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *r = (resource_t *) lpc->data; clear_bit(r->flags, pe_rsc_allocating); } show_colocation(rsc, TRUE, rsc_cmd == 'A', 1); fprintf(stdout, "* %s\n", rsc->id); show_location(rsc, NULL); for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *r = (resource_t *) lpc->data; clear_bit(r->flags, pe_rsc_allocating); } show_colocation(rsc, FALSE, rsc_cmd == 'A', 1); } else if (rsc_cmd == 'c') { int found = 0; GListPtr lpc = NULL; rc = pcmk_ok; for (lpc = data_set.resources; lpc != NULL; lpc = lpc->next) { resource_t *rsc = (resource_t *) lpc->data; print_cts_rsc(rsc); found++; } print_cts_constraints(&data_set); } else if (rsc_cmd == 'F') { rc = fail_lrm_rsc(crmd_channel, host_uname, rsc_id, &data_set); if (rc == pcmk_ok) { start_mainloop(); } } else if (rsc_cmd == 'O') { rc = list_resource_operations(rsc_id, host_uname, TRUE, &data_set); } else if (rsc_cmd == 'o') { rc = list_resource_operations(rsc_id, host_uname, FALSE, &data_set); } else if (rc == -ENXIO) { CMD_ERR("Resource '%s' not found: %s\n", crm_str(rsc_id), pcmk_strerror(rc)); } else if (rsc_cmd == 'W') { if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } rc = do_find_resource(rsc_id, NULL, &data_set); } else if (rsc_cmd == 'q') { if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } rc = dump_resource(rsc_id, &data_set, TRUE); } else if (rsc_cmd == 'w') { if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } rc = dump_resource(rsc_id, &data_set, FALSE); } else if (rsc_cmd == 'U') { node_t *dest = NULL; if (rsc_id == NULL) { CMD_ERR("No value specified for --resource\n"); rc = -ENXIO; goto bail; } if (host_uname) { dest = pe_find_node(data_set.nodes, host_uname); if (dest == NULL) { CMD_ERR("Unknown node: %s\n", host_uname); rc = -ENXIO; goto bail; } rc = clear_resource(rsc_id, dest->details->uname, NULL, cib_conn); } else { rc = clear_resource(rsc_id, NULL, data_set.nodes, cib_conn); } } else if (rsc_cmd == 'M' && host_uname) { int count = 0; node_t *current = NULL; node_t *dest = pe_find_node(data_set.nodes, host_uname); resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); gboolean cur_is_dest = FALSE; rc = -EINVAL; if (rsc == NULL) { CMD_ERR("Resource '%s' not moved: not found\n", rsc_id); rc = -ENXIO; goto bail; } else if (scope_master && rsc->variant < pe_master) { resource_t *p = uber_parent(rsc); if(p->variant == pe_master) { CMD_ERR("Using parent '%s' for --move command instead of '%s'.\n", rsc->id, rsc_id); rsc_id = p->id; rsc = p; } else { CMD_ERR("Ignoring '--master' option: not valid for %s resources.\n", get_resource_typename(rsc->variant)); scope_master = FALSE; } } if(rsc->variant == pe_master) { GListPtr iter = NULL; for(iter = rsc->children; iter; iter = iter->next) { resource_t *child = (resource_t *)iter->data; enum rsc_role_e child_role = child->fns->state(child, TRUE); if(child_role == RSC_ROLE_MASTER) { rsc = child; count++; } } if(scope_master == FALSE && count == 0) { count = g_list_length(rsc->running_on); } } else if (rsc->variant > pe_group) { count = g_list_length(rsc->running_on); } else if (g_list_length(rsc->running_on) > 1) { CMD_ERR("Resource '%s' not moved: active on multiple nodes\n", rsc_id); goto bail; } if(dest == NULL) { CMD_ERR("Error performing operation: node '%s' is unknown\n", host_uname); rc = -ENXIO; goto bail; } if(g_list_length(rsc->running_on) == 1) { current = rsc->running_on->data; } if(current == NULL) { /* Nothing to check */ } else if(scope_master && rsc->fns->state(rsc, TRUE) != RSC_ROLE_MASTER) { crm_trace("%s is already active on %s but not in correct state", rsc_id, dest->details->uname); } else if (safe_str_eq(current->details->uname, dest->details->uname)) { cur_is_dest = TRUE; if (do_force) { crm_info("%s is already %s on %s, reinforcing placement with location constraint.\n", rsc_id, scope_master?"promoted":"active", dest->details->uname); } else { CMD_ERR("Error performing operation: %s is already %s on %s\n", rsc_id, scope_master?"promoted":"active", dest->details->uname); goto bail; } } /* Clear any previous constraints for 'dest' */ clear_resource(rsc_id, dest->details->uname, data_set.nodes, cib_conn); /* Record an explicit preference for 'dest' */ rc = prefer_resource(rsc_id, dest->details->uname, cib_conn); crm_trace("%s%s now prefers node %s%s", rsc->id, scope_master?" (master)":"", dest->details->uname, do_force?"(forced)":""); /* only ban the previous location if current location != destination location. * it is possible to use -M to enforce a location without regard of where the * resource is currently located */ if(do_force && (cur_is_dest == FALSE)) { /* Ban the original location if possible */ if(current) { ban_resource(rsc_id, current->details->uname, NULL, cib_conn); } else if(count > 1) { CMD_ERR("Resource '%s' is currently %s in %d locations. One may now move one to %s\n", rsc_id, scope_master?"promoted":"active", count, dest->details->uname); CMD_ERR("You can prevent '%s' from being %s at a specific location with:" " --ban %s--host \n", rsc_id, scope_master?"promoted":"active", scope_master?"--master ":""); } else { crm_trace("Not banning %s from it's current location: not active", rsc_id); } } } else if (rsc_cmd == 'B' && host_uname) { resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); node_t *dest = pe_find_node(data_set.nodes, host_uname); rc = -ENXIO; if (rsc_id == NULL) { CMD_ERR("No value specified for --resource\n"); goto bail; } else if(rsc == NULL) { CMD_ERR("Resource '%s' not moved: unknown\n", rsc_id); } else if (dest == NULL) { CMD_ERR("Error performing operation: node '%s' is unknown\n", host_uname); goto bail; } rc = ban_resource(rsc_id, dest->details->uname, NULL, cib_conn); } else if (rsc_cmd == 'B' || rsc_cmd == 'M') { resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); rc = -ENXIO; if (rsc_id == NULL) { CMD_ERR("No value specified for --resource\n"); goto bail; } rc = -EINVAL; if(rsc == NULL) { CMD_ERR("Resource '%s' not moved: unknown\n", rsc_id); } else if(g_list_length(rsc->running_on) == 1) { node_t *current = rsc->running_on->data; rc = ban_resource(rsc_id, current->details->uname, NULL, cib_conn); } else if(rsc->variant == pe_master) { int count = 0; GListPtr iter = NULL; node_t *current = NULL; for(iter = rsc->children; iter; iter = iter->next) { resource_t *child = (resource_t *)iter->data; enum rsc_role_e child_role = child->fns->state(child, TRUE); if(child_role == RSC_ROLE_MASTER) { count++; current = child->running_on->data; } } if(count == 1 && current) { rc = ban_resource(rsc_id, current->details->uname, NULL, cib_conn); } else { CMD_ERR("Resource '%s' not moved: active in %d locations (promoted in %d).\n", rsc_id, g_list_length(rsc->running_on), count); CMD_ERR("You can prevent '%s' from running on a specific location with: --ban --host \n", rsc_id); CMD_ERR("You can prevent '%s' from being promoted at a specific location with:" " --ban --master --host \n", rsc_id); } } else { CMD_ERR("Resource '%s' not moved: active in %d locations.\n", rsc_id, g_list_length(rsc->running_on)); CMD_ERR("You can prevent '%s' from running on a specific location with: --ban --host \n", rsc_id); } } else if (rsc_cmd == 'G') { if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } rc = dump_resource_prop(rsc_id, prop_name, &data_set); } else if (rsc_cmd == 'S') { xmlNode *msg_data = NULL; if (prop_value == NULL || strlen(prop_value) == 0) { CMD_ERR("You need to supply a value with the -v option\n"); rc = -EINVAL; goto bail; } else if (cib_conn == NULL) { rc = -ENOTCONN; goto bail; } if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } CRM_LOG_ASSERT(rsc_type != NULL); CRM_LOG_ASSERT(prop_name != NULL); CRM_LOG_ASSERT(prop_value != NULL); msg_data = create_xml_node(NULL, rsc_type); crm_xml_add(msg_data, XML_ATTR_ID, rsc_id); crm_xml_add(msg_data, prop_name, prop_value); rc = cib_conn->cmds->modify(cib_conn, XML_CIB_TAG_RESOURCES, msg_data, cib_options); free_xml(msg_data); } else if (rsc_cmd == 'g') { if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } rc = dump_resource_attr(rsc_id, prop_name, &data_set); } else if (rsc_cmd == 'p') { if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } if (prop_value == NULL || strlen(prop_value) == 0) { CMD_ERR("You need to supply a value with the -v option\n"); rc = -EINVAL; goto bail; } /* coverity[var_deref_model] False positive */ rc = set_resource_attr(rsc_id, prop_set, prop_id, prop_name, prop_value, recursive, cib_conn, &data_set); } else if (rsc_cmd == 'd') { if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } /* coverity[var_deref_model] False positive */ rc = delete_resource_attr(rsc_id, prop_set, prop_id, prop_name, cib_conn, &data_set); } else if (rsc_cmd == 'C' && rsc_id) { resource_t *rsc = pe_find_resource(data_set.resources, rsc_id); crm_debug("Re-checking the state of %s on %s", rsc_id, host_uname); if(rsc) { crmd_replies_needed = 0; rc = delete_lrm_rsc(cib_conn, crmd_channel, host_uname, rsc, &data_set); } else { rc = -ENODEV; } if (rc == pcmk_ok) { start_mainloop(); } } else if (rsc_cmd == 'C') { #if HAVE_ATOMIC_ATTRD xmlNode *cmd = create_request(CRM_OP_REPROBE, NULL, host_uname, CRM_SYSTEM_CRMD, crm_system_name, our_pid); crm_debug("Re-checking the state of all resources on %s", host_uname?host_uname:"all nodes"); rc = attrd_update_delegate( NULL, 'u', host_uname, "fail-count-*", NULL, XML_CIB_TAG_STATUS, NULL, NULL, NULL, FALSE); if (crm_ipc_send(crmd_channel, cmd, 0, 0, NULL) > 0) { start_mainloop(); } free_xml(cmd); #else GListPtr rIter = NULL; crmd_replies_needed = 0; for (rIter = data_set.resources; rIter; rIter = rIter->next) { resource_t *rsc = rIter->data; delete_lrm_rsc(cib_conn, crmd_channel, host_uname, rsc, &data_set); } start_mainloop(); #endif } else if (rsc_cmd == 'D') { xmlNode *msg_data = NULL; if (rsc_id == NULL) { CMD_ERR("Must supply a resource id with -r\n"); rc = -ENXIO; goto bail; } if (rsc_type == NULL) { CMD_ERR("You need to specify a resource type with -t"); rc = -ENXIO; goto bail; } else if (cib_conn == NULL) { rc = -ENOTCONN; goto bail; } msg_data = create_xml_node(NULL, rsc_type); crm_xml_add(msg_data, XML_ATTR_ID, rsc_id); rc = cib_conn->cmds->delete(cib_conn, XML_CIB_TAG_RESOURCES, msg_data, cib_options); free_xml(msg_data); } else { CMD_ERR("Unknown command: %c\n", rsc_cmd); } bail: if (data_set.input != NULL) { cleanup_alloc_calculations(&data_set); } if (cib_conn != NULL) { cib_conn->cmds->signoff(cib_conn); cib_delete(cib_conn); } if (rc == -pcmk_err_no_quorum) { CMD_ERR("Error performing operation: %s\n", pcmk_strerror(rc)); CMD_ERR("Try using -f\n"); } else if (rc != pcmk_ok) { CMD_ERR("Error performing operation: %s\n", pcmk_strerror(rc)); } return crm_exit(rc); } diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c index 7c0dcc77ce..f6bb72ce8c 100644 --- a/tools/crm_simulate.c +++ b/tools/crm_simulate.c @@ -1,1651 +1,870 @@ /* * 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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include "fake_transition.h" cib_t *global_cib = NULL; GListPtr op_fail = NULL; gboolean quiet = FALSE; gboolean bringing_nodes_online = FALSE; gboolean print_pending = FALSE; char *temp_shadow = NULL; -#define new_node_template "//"XML_CIB_TAG_NODE"[@uname='%s']" -#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 quiet_log(fmt, args...) do { \ if(quiet == FALSE) { \ printf(fmt , ##args); \ } \ } while(0) extern void cleanup_alloc_calculations(pe_working_set_t * data_set); extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now); char *use_date = NULL; static crm_time_t * get_date(void) { if (use_date) { return crm_time_new(use_date); } return NULL; } -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; - - xpath = calloc(1, max); - - snprintf(xpath, max, rsc_template, node, resource); - match = get_xpath_object(xpath, cib_node, LOG_DEBUG_2); - - free(xpath); - return match; -} - -static void -create_node_entry(cib_t * cib_conn, const char *node) -{ - int rc = pcmk_ok; - int max = strlen(new_node_template) + strlen(node) + 1; - char *xpath = NULL; - - xpath = calloc(1, max); - - snprintf(xpath, max, new_node_template, node); - rc = cib_conn->cmds->query(cib_conn, xpath, NULL, cib_xpath | cib_sync_call | cib_scope_local); - - if (rc == -ENXIO) { - xmlNode *cib_object = create_xml_node(NULL, XML_CIB_TAG_NODE); - - /* Using node uname as uuid ala corosync/openais */ - crm_xml_add(cib_object, XML_ATTR_ID, node); - crm_xml_add(cib_object, XML_ATTR_UNAME, node); - cib_conn->cmds->create(cib_conn, XML_CIB_TAG_NODES, cib_object, - cib_sync_call | cib_scope_local); - /* Not bothering with subsequent query to see if it exists, - we'll bomb out later in the call to query_node_uuid()... */ - - free_xml(cib_object); - } - - free(xpath); -} - -static xmlNode * -inject_node_state(cib_t * cib_conn, const char *node, const char *uuid) -{ - int rc = pcmk_ok; - int max = strlen(rsc_template) + strlen(node) + 1; - char *xpath = NULL; - xmlNode *cib_object = NULL; - - xpath = calloc(1, max); - - if (bringing_nodes_online) { - create_node_entry(cib_conn, node); - } - - 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 (cib_object && ID(cib_object) == NULL) { - crm_err("Detected multiple node_state entries for xpath=%s, bailing", xpath); - crm_log_xml_warn(cib_object, "Duplicates"); - crm_exit(ENOTUNIQ); - } - - if (rc == -ENXIO) { - char *found_uuid = NULL; - - if (uuid == NULL) { - query_node_uuid(cib_conn, node, &found_uuid, NULL); - } else { - found_uuid = strdup(uuid); - } - - cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); - crm_xml_add(cib_object, XML_ATTR_UUID, found_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); - free_xml(cib_object); - free(found_uuid); - - rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, - cib_xpath | cib_sync_call | cib_scope_local); - crm_trace("injecting node state for %s. rc is %d", node, rc); - } - - free(xpath); - CRM_ASSERT(rc == pcmk_ok); - return cib_object; -} - -static xmlNode * -modify_node(cib_t * cib_conn, char *node, gboolean up) -{ - xmlNode *cib_node = inject_node_state(cib_conn, node, NULL); - - if (up) { - crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_YES); - crm_xml_add(cib_node, XML_NODE_IS_PEER, ONLINESTATUS); - crm_xml_add(cib_node, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_MEMBER); - crm_xml_add(cib_node, XML_NODE_EXPECTED, CRMD_JOINSTATE_MEMBER); - - } else { - crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_NO); - crm_xml_add(cib_node, XML_NODE_IS_PEER, OFFLINESTATUS); - crm_xml_add(cib_node, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_DOWN); - crm_xml_add(cib_node, XML_NODE_EXPECTED, CRMD_JOINSTATE_DOWN); - } - - crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name); - return cib_node; -} - -static void -inject_transient_attr(xmlNode * cib_node, const char *name, const char *value) -{ - xmlNode *attrs = NULL; - xmlNode *container = NULL; - xmlNode *nvp = NULL; - const char *node_uuid = ID(cib_node); - char *nvp_id = crm_concat(name, node_uuid, '-'); - - crm_info("Injecting attribute %s=%s into %s '%s'", name, value, xmlGetNodePath(cib_node), - ID(cib_node)); - - attrs = first_named_child(cib_node, XML_TAG_TRANSIENT_NODEATTRS); - if (attrs == NULL) { - attrs = create_xml_node(cib_node, XML_TAG_TRANSIENT_NODEATTRS); - crm_xml_add(attrs, XML_ATTR_ID, node_uuid); - } - - container = first_named_child(attrs, XML_TAG_ATTR_SETS); - if (container == NULL) { - container = create_xml_node(attrs, XML_TAG_ATTR_SETS); - crm_xml_add(container, XML_ATTR_ID, node_uuid); - } - - nvp = create_xml_node(container, XML_CIB_TAG_NVPAIR); - crm_xml_add(nvp, XML_ATTR_ID, nvp_id); - crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name); - crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value); - - free(nvp_id); -} - -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; - char *xpath = 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, "stonith") - && safe_str_neq(rclass, "heartbeat") - && safe_str_neq(rclass, "service") - && safe_str_neq(rclass, "upstart") - && safe_str_neq(rclass, "systemd") - && 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; - } - - xpath = (char *)xmlGetNodePath(cib_node); - crm_info("Injecting new resource %s into %s '%s'", resource, xpath, ID(cib_node)); - free(xpath); - - 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 lrmd_event_data_t * -create_op(xmlNode * cib_resource, const char *task, int interval, int outcome) -{ - lrmd_event_data_t *op = NULL; - xmlNode *xop = NULL; - - op = calloc(1, sizeof(lrmd_event_data_t)); - - op->rsc_id = strdup(ID(cib_resource)); - op->interval = interval; - op->op_type = strdup(task); - - op->rc = outcome; - op->op_status = 0; - op->params = NULL; /* TODO: Fill me in */ - op->t_run = time(NULL); - op->t_rcchange = op->t_run; - - op->call_id = 0; - for (xop = __xml_first_child(cib_resource); xop != NULL; xop = __xml_next(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, lrmd_event_data_t * op, int target_rc) -{ - return create_operation_update(cib_resource, op, CRM_FEATURE_SET, target_rc, crm_system_name, - LOG_DEBUG_2); -} - -static void -update_failcounts(xmlNode * cib_node, const char *resource, int interval, int rc) -{ - if (rc == 0) { - return; - - } else if (rc == 7 && interval == 0) { - return; - - } else { - char *name = NULL; - char *now = crm_itoa(time(NULL)); - - name = crm_concat("fail-count", resource, '-'); - inject_transient_attr(cib_node, name, "value++"); - - name = crm_concat("last-failure", resource, '-'); - inject_transient_attr(cib_node, name, now); - - free(name); - free(now); - } -} - -static gboolean -exec_pseudo_action(crm_graph_t * graph, crm_action_t * action) -{ - const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); - const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY); - - action->confirmed = TRUE; - - quiet_log(" * Pseudo action: %s%s%s\n", task, node ? " on " : "", node ? node : ""); - update_graph(graph, action); - return TRUE; -} - -GListPtr resource_list = NULL; - -static gboolean -exec_rsc_action(crm_graph_t * graph, crm_action_t * action) -{ - int rc = 0; - GListPtr gIter = NULL; - lrmd_event_data_t *op = NULL; - int target_outcome = 0; - gboolean uname_is_uuid = FALSE; - - const char *rtype = NULL; - const char *rclass = NULL; - const char *resource = NULL; - const char *rprovider = NULL; - const char *operation = crm_element_value(action->xml, "operation"); - const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC); - - xmlNode *cib_node = 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); - char *uuid = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET_UUID); - const char *router_node = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE); - - if (safe_str_eq(operation, CRM_OP_PROBED) - || safe_str_eq(operation, CRM_OP_REPROBE)) { - crm_info("Skipping %s op for %s\n", operation, node); - goto done; - } - - if (action_rsc == NULL) { - crm_log_xml_err(action->xml, "Bad"); - free(node); free(uuid); - return FALSE; - } - - /* Look for the preferred name - * If not found, try the expected 'local' name - * If not found use the preferred name anyway - */ - resource = crm_element_value(action_rsc, XML_ATTR_ID); - if (pe_find_resource(resource_list, resource) == NULL) { - const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG); - - if (pe_find_resource(resource_list, longname)) { - resource = longname; - } - } - - if (safe_str_eq(operation, "delete")) { - quiet_log(" * Resource action: %-15s delete on %s\n", resource, node); - goto done; - } - - 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, NULL, cib_sync_call | cib_scope_local) == - pcmk_ok); - - if (router_node) { - uname_is_uuid = TRUE; - } - - cib_node = inject_node_state(global_cib, node, uname_is_uuid ? node : uuid); - 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); - if (op->interval) { - quiet_log(" * Resource action: %-15s %s=%d on %s\n", resource, op->op_type, op->interval, - node); - } else { - quiet_log(" * Resource action: %-15s %s on %s\n", resource, op->op_type, node); - } - - for (gIter = op_fail; gIter != NULL; gIter = gIter->next) { - char *spec = (char *)gIter->data; - char *key = NULL; - - key = calloc(1, 1 + strlen(spec)); - snprintf(key, strlen(spec), "%s_%s_%d@%s=", resource, op->op_type, op->interval, node); - - if (strncasecmp(key, spec, strlen(key)) == 0) { - sscanf(spec, "%*[^=]=%d", (int *)&op->rc); - - action->failed = TRUE; - graph->abort_priority = INFINITY; - printf("\tPretending action %d failed with rc=%d\n", action->id, op->rc); - update_failcounts(cib_node, resource, op->interval, op->rc); - free(key); - break; - } - free(key); - } - - inject_op(cib_resource, op, target_outcome); - lrmd_free_event(op); - - rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, - cib_sync_call | cib_scope_local); - CRM_ASSERT(rc == pcmk_ok); - - done: - free(node); free(uuid); - free_xml(cib_node); - action->confirmed = TRUE; - update_graph(graph, action); - return TRUE; -} - -static gboolean -exec_crmd_action(crm_graph_t * graph, crm_action_t * action) -{ - const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); - const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK); - xmlNode *rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); - - action->confirmed = TRUE; - - if(rsc) { - quiet_log(" * Cluster action: %s for %s on %s\n", task, ID(rsc), node); - } else { - quiet_log(" * Cluster action: %s on %s\n", task, node); - } - update_graph(graph, action); - return TRUE; -} - -#define STATUS_PATH_MAX 512 -static gboolean -exec_stonith_action(crm_graph_t * graph, crm_action_t * action) -{ - const char *op = crm_meta_value(action->params, "stonith_action"); - char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); - - quiet_log(" * Fencing %s (%s)\n", target, op); - if(safe_str_neq(op, "on")) { - int rc = 0; - char xpath[STATUS_PATH_MAX]; - xmlNode *cib_node = modify_node(global_cib, target, FALSE); - - crm_xml_add(cib_node, XML_ATTR_ORIGIN, __FUNCTION__); - CRM_ASSERT(cib_node != NULL); - - rc = global_cib->cmds->replace(global_cib, XML_CIB_TAG_STATUS, cib_node, - cib_sync_call | cib_scope_local); - CRM_ASSERT(rc == pcmk_ok); - - snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM); - 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); - global_cib->cmds->delete(global_cib, xpath, NULL, - cib_xpath | cib_sync_call | cib_scope_local); - - free_xml(cib_node); - } - - action->confirmed = TRUE; - update_graph(graph, action); - free(target); - return TRUE; -} - static void print_cluster_status(pe_working_set_t * data_set, long options) { char *online_nodes = NULL; char *online_remote_nodes = NULL; char *online_remote_containers = NULL; char *offline_nodes = NULL; char *offline_remote_nodes = NULL; GListPtr gIter = NULL; for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; const char *node_mode = NULL; char *node_name = NULL; if (is_container_remote_node(node)) { node_name = g_strdup_printf("%s:%s", node->details->uname, node->details->remote_rsc->container->id); } else { node_name = g_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 (is_container_remote_node(node)) { online_remote_containers = add_list_element(online_remote_containers, node_name); } else if (is_baremetal_remote_node(node)) { online_remote_nodes = add_list_element(online_remote_nodes, node_name); } else { online_nodes = add_list_element(online_nodes, node_name); } free(node_name); continue; } else { if (is_baremetal_remote_node(node)) { offline_remote_nodes = add_list_element(offline_remote_nodes, node_name); } else if (is_container_remote_node(node)) { /* ignore offline container nodes */ } else { offline_nodes = add_list_element(offline_nodes, node_name); } free(node_name); continue; } if (is_container_remote_node(node)) { printf("ContainerNode %s: %s\n", node_name, node_mode); } else if (is_baremetal_remote_node(node)) { printf("RemoteNode %s: %s\n", node_name, node_mode); } else if (safe_str_eq(node->details->uname, node->details->id)) { 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_remote_containers) { printf("Containers: [%s ]\n", online_remote_containers); free(online_remote_containers); } fprintf(stdout, "\n"); for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; if (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 int -run_simulation(pe_working_set_t * data_set) -{ - 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); - - quiet_log("\nExecuting cluster transition:\n"); - transition = unpack_graph(data_set->graph, crm_system_name); - print_graph(LOG_DEBUG, transition); - - resource_list = data_set->resources; - do { - graph_rc = run_graph(transition); - - } while (graph_rc == transition_active); - resource_list = NULL; - - if (graph_rc != transition_complete) { - fprintf(stdout, "Transition failed: %s\n", transition_status(graph_rc)); - print_graph(LOG_ERR, transition); - } - destroy_graph(transition); - if (graph_rc != transition_complete) { - fprintf(stdout, "An invalid transition was produced\n"); - } - - if (quiet == FALSE) { - xmlNode *cib_object = NULL; - int rc = - global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call | cib_scope_local); - - CRM_ASSERT(rc == pcmk_ok); - quiet_log("\nRevised cluster status:\n"); - cleanup_alloc_calculations(data_set); - data_set->input = cib_object; - data_set->now = get_date(); - - cluster_status(data_set); - print_cluster_status(data_set, 0); - } - - if (graph_rc != transition_complete) { - return graph_rc; - } - return 0; -} - static char * create_action_name(action_t * action) { char *action_name = NULL; const char *prefix = NULL; const char *action_host = NULL; const char *task = action->task; if (action->node) { action_host = action->node->details->uname; } else if (is_not_set(action->flags, pe_action_pseudo)) { action_host = ""; } if (safe_str_eq(action->task, RSC_CANCEL)) { prefix = "Cancel "; task = "monitor"; /* TO-DO: Hack! */ } if (action->rsc && action->rsc->clone_name) { char *key = NULL; const char *name = action->rsc->clone_name; const char *interval_s = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); int interval = crm_parse_int(interval_s, "0"); if (safe_str_eq(action->task, RSC_NOTIFY) || safe_str_eq(action->task, RSC_NOTIFIED)) { 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 = generate_notify_key(name, n_type, n_task); } else { key = generate_op_key(name, task, interval); } if (action_host) { action_name = g_strdup_printf("%s%s %s", prefix ? prefix : "", key, action_host); } else { action_name = g_strdup_printf("%s%s", prefix ? prefix : "", key); } free(key); } else if (safe_str_eq(action->task, CRM_OP_FENCE)) { const char *op = g_hash_table_lookup(action->meta, "stonith_action"); action_name = g_strdup_printf("%s%s '%s' %s", prefix ? prefix : "", action->task, op, action_host); } else if (action->rsc && action_host) { action_name = g_strdup_printf("%s%s %s", prefix ? prefix : "", action->uuid, action_host); } else if (action_host) { action_name = g_strdup_printf("%s%s %s", prefix ? prefix : "", action->task, action_host); } else { action_name = g_strdup_printf("%s", action->uuid); } return action_name; } static void create_dotfile(pe_working_set_t * data_set, const char *dot_file, gboolean all_actions) { GListPtr gIter = NULL; 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"); for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (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 (is_set(action->flags, pe_action_pseudo)) { font = "orange"; } 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 = "red"; font = "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); 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); dont_write: free(action_name); } for (gIter = data_set->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; GListPtr gIter2 = NULL; for (gIter2 = action->actions_before; gIter2 != NULL; gIter2 = gIter2->next) { action_wrapper_t *before = (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 (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) && 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"); if (dot_strm != NULL) { fflush(dot_strm); fclose(dot_strm); } } -static int -find_ticket_state(cib_t * the_cib, const char *ticket_id, xmlNode ** ticket_state_xml) -{ - int offset = 0; - static int xpath_max = 1024; - int rc = pcmk_ok; - xmlNode *xml_search = NULL; - - char *xpath_string = NULL; - - CRM_ASSERT(ticket_state_xml != NULL); - *ticket_state_xml = NULL; - - xpath_string = calloc(1, xpath_max); - offset += snprintf(xpath_string + offset, xpath_max - offset, "%s", "/cib/status/tickets"); - - if (ticket_id) { - offset += snprintf(xpath_string + offset, xpath_max - offset, "/%s[@id=\"%s\"]", - XML_CIB_TAG_TICKET_STATE, ticket_id); - } - CRM_LOG_ASSERT(offset > 0); - rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search, - cib_sync_call | cib_scope_local | cib_xpath); - - if (rc != pcmk_ok) { - goto bail; - } - - crm_log_xml_debug(xml_search, "Match"); - if (xml_has_children(xml_search)) { - if (ticket_id) { - fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id); - } - *ticket_state_xml = xml_search; - } else { - *ticket_state_xml = xml_search; - } - - bail: - free(xpath_string); - return rc; -} - -static int -set_ticket_state_attr(const char *ticket_id, const char *attr_name, - const char *attr_value, cib_t * cib, int cib_options) -{ - int rc = pcmk_ok; - xmlNode *xml_top = NULL; - xmlNode *ticket_state_xml = NULL; - - rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); - if (rc == pcmk_ok) { - crm_debug("Found a match state for ticket: id=%s", ticket_id); - xml_top = ticket_state_xml; - - } else if (rc != -ENXIO) { - return rc; - - } else { - xmlNode *xml_obj = NULL; - - xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS); - xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS); - ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE); - crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id); - } - - crm_xml_add(ticket_state_xml, attr_name, attr_value); - - crm_log_xml_debug(xml_top, "Update"); - - rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options); - - free_xml(xml_top); - - return rc; -} - -static void -modify_configuration(pe_working_set_t * data_set, - const char *quorum, const char *watchdog, GListPtr node_up, GListPtr node_down, GListPtr node_fail, - GListPtr op_inject, GListPtr ticket_grant, GListPtr ticket_revoke, - GListPtr ticket_standby, GListPtr ticket_activate) -{ - int rc = pcmk_ok; - GListPtr gIter = NULL; - - xmlNode *cib_op = NULL; - xmlNode *cib_node = NULL; - xmlNode *cib_resource = NULL; - - lrmd_event_data_t *op = NULL; - - if (quorum) { - xmlNode *top = create_xml_node(NULL, XML_TAG_CIB); - - 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(global_cib, NULL, top, cib_sync_call | cib_scope_local); - CRM_ASSERT(rc == pcmk_ok); - } - - if (watchdog) { - quiet_log(" + Setting watchdog: %s\n", watchdog); - - rc = update_attr_delegate(global_cib, cib_sync_call | cib_scope_local, - XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL, - XML_ATTR_HAVE_WATCHDOG, watchdog, FALSE, NULL, NULL); - - CRM_ASSERT(rc == pcmk_ok); - } - - for (gIter = node_up; gIter != NULL; gIter = gIter->next) { - char *node = (char *)gIter->data; - - quiet_log(" + Bringing node %s online\n", node); - cib_node = modify_node(global_cib, 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 == pcmk_ok); - free_xml(cib_node); - } - - for (gIter = node_down; gIter != NULL; gIter = gIter->next) { - char xpath[STATUS_PATH_MAX]; - char *node = (char *)gIter->data; - - quiet_log(" + Taking node %s offline\n", node); - cib_node = modify_node(global_cib, 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 == pcmk_ok); - free_xml(cib_node); - - snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", node, XML_CIB_TAG_LRM); - 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", node, - XML_TAG_TRANSIENT_NODEATTRS); - global_cib->cmds->delete(global_cib, xpath, NULL, - cib_xpath | cib_sync_call | cib_scope_local); - - } - - for (gIter = node_fail; gIter != NULL; gIter = gIter->next) { - char *node = (char *)gIter->data; - - quiet_log(" + Failing node %s\n", node); - cib_node = modify_node(global_cib, node, TRUE); - crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, 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 == pcmk_ok); - free_xml(cib_node); - } - - for (gIter = ticket_grant; gIter != NULL; gIter = gIter->next) { - char *ticket_id = (char *)gIter->data; - - quiet_log(" + Granting ticket %s\n", ticket_id); - rc = set_ticket_state_attr(ticket_id, "granted", "true", - global_cib, cib_sync_call | cib_scope_local); - - CRM_ASSERT(rc == pcmk_ok); - } - - for (gIter = ticket_revoke; gIter != NULL; gIter = gIter->next) { - char *ticket_id = (char *)gIter->data; - - quiet_log(" + Revoking ticket %s\n", ticket_id); - rc = set_ticket_state_attr(ticket_id, "granted", "false", - global_cib, cib_sync_call | cib_scope_local); - - CRM_ASSERT(rc == pcmk_ok); - } - - for (gIter = ticket_standby; gIter != NULL; gIter = gIter->next) { - char *ticket_id = (char *)gIter->data; - - quiet_log(" + Making ticket %s standby\n", ticket_id); - rc = set_ticket_state_attr(ticket_id, "standby", "true", - global_cib, cib_sync_call | cib_scope_local); - - CRM_ASSERT(rc == pcmk_ok); - } - - for (gIter = ticket_activate; gIter != NULL; gIter = gIter->next) { - char *ticket_id = (char *)gIter->data; - - quiet_log(" + Activating ticket %s\n", ticket_id); - rc = set_ticket_state_attr(ticket_id, "standby", "false", - global_cib, cib_sync_call | cib_scope_local); - - CRM_ASSERT(rc == pcmk_ok); - } - - for (gIter = op_inject; gIter != NULL; gIter = gIter->next) { - char *spec = (char *)gIter->data; - - 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; - - quiet_log(" + Injecting %s into the configuration\n", spec); - - key = calloc(1, strlen(spec) + 1); - node = calloc(1, strlen(spec) + 1); - 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); - if (rsc == NULL) { - fprintf(stderr, " - Invalid resource name: %s\n", resource); - } else { - 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_state(global_cib, node, NULL); - CRM_ASSERT(cib_node != NULL); - - update_failcounts(cib_node, resource, interval, outcome); - - 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); - lrmd_free_event(op); - - rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, - cib_sync_call | cib_scope_local); - CRM_ASSERT(rc == pcmk_ok); - } - free(task); - free(node); - free(key); - } -} - static void setup_input(const char *input, const char *output) { int rc = pcmk_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); if (rc == pcmk_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_ok) { fprintf(stderr, "Live CIB query failed: %s (%d)\n", pcmk_strerror(rc), rc); crm_exit(rc); } else if (cib_object == NULL) { fprintf(stderr, "Live CIB query failed: empty result\n"); crm_exit(ENOTCONN); } } else if (safe_str_eq(input, "-")) { 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); crm_exit(ENOKEY); } if (validate_xml(cib_object, NULL, FALSE) != TRUE) { free_xml(cib_object); crm_exit(pcmk_err_schema_validation); } if (output == NULL) { char *pid = crm_itoa(getpid()); 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) { fprintf(stderr, "Could not create '%s': %s\n", output, strerror(errno)); crm_exit(rc); } setenv("CIB_file", output, 1); free(local_output); } /* *INDENT-OFF* */ static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "\tThis text"}, {"version", 0, 0, '$', "\tVersion information" }, {"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"}, {"show-utilization", 0, 0, 'U', "Show utilization information"}, {"profile", 1, 0, 'P', "Run all tests in the named directory to create profiling data"}, {"pending", 0, 0, 'j', "\tDisplay pending state if 'record-pending' is enabled"}, {"-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', "\tGenerate a failure for the cluster to react to in the simulation"}, {"-spacer-", 0, 0, '-', "\t\tValue is of the form ${resource}_${task}_${interval}@${node}=${rc}."}, {"-spacer-", 0, 0, '-', "\t\tEg. memcached_monitor_20000@bart.example.com=7"}, {"-spacer-", 0, 0, '-', "\t\tFor more information on OCF return codes, refer to: http://www.clusterlabs.org/doc/en-US/Pacemaker/1.1/html/Pacemaker_Explained/s-ocf-return-codes.html"}, {"op-fail", 1, 0, 'F', "\tIf the specified task occurs during the simulation, have it fail with return code ${rc}"}, {"-spacer-", 0, 0, '-', "\t\tValue is of the form ${resource}_${task}_${interval}@${node}=${rc}."}, {"-spacer-", 0, 0, '-', "\t\tEg. memcached_stop_0@bart.example.com=1\n"}, {"-spacer-", 0, 0, '-', "\t\tThe transition will normally stop at the failed action. Save the result with --save-output and re-run with --xml-file"}, {"set-datetime", 1, 0, 't', "Set date/time"}, {"quorum", 1, 0, 'q', "\tSpecify a value for quorum"}, {"watchdog", 1, 0, 'w', "\tAssume a watchdog device is active"}, {"ticket-grant", 1, 0, 'g', "Grant a ticket"}, {"ticket-revoke", 1, 0, 'r', "Revoke a ticket"}, {"ticket-standby", 1, 0, 'b', "Make a ticket standby"}, {"ticket-activate", 1, 0, 'e', "Activate a ticket"}, {"-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"}, {"-spacer-", 0, 0, '-', "\nExamples:\n"}, {"-spacer-", 0, 0, '-', "Pretend a recurring monitor action found memcached stopped on node fred.example.com and, during recovery, that the memcached stop action failed", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " crm_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", pcmk_option_example}, {"-spacer-", 0, 0, '-', "Now see what the reaction to the stop failure would be", pcmk_option_paragraph}, {"-spacer-", 0, 0, '-', " crm_simulate -S --xml-file /tmp/memcached-test.xml", pcmk_option_example}, {0, 0, 0, 0} }; /* *INDENT-ON* */ static void profile_one(const char *xml_file) { xmlNode *cib_object = NULL; pe_working_set_t data_set; printf("* Testing %s\n", xml_file); cib_object = filename2xml(xml_file); 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; } set_working_set_defaults(&data_set); data_set.input = cib_object; data_set.now = get_date(); do_calculations(&data_set, cib_object, NULL); cleanup_alloc_calculations(&data_set); } #ifndef FILENAME_MAX # define FILENAME_MAX 512 #endif static int profile_all(const char *dir) { struct dirent **namelist; int lpc = 0; int file_num = scandir(dir, &namelist, 0, alphasort); if (file_num > 0) { struct stat prop; char buffer[FILENAME_MAX + 1]; while (file_num--) { if ('.' == namelist[file_num]->d_name[0]) { free(namelist[file_num]); continue; } else if (strstr(namelist[file_num]->d_name, ".xml") == NULL) { free(namelist[file_num]); continue; } lpc++; snprintf(buffer, FILENAME_MAX, "%s/%s", dir, namelist[file_num]->d_name); if (stat(buffer, &prop) == 0 && S_ISREG(prop.st_mode)) { profile_one(buffer); } free(namelist[file_num]); } free(namelist); } return lpc; } int main(int argc, char **argv) { int rc = 0; guint modified = 0; gboolean store = FALSE; gboolean process = FALSE; gboolean simulate = FALSE; gboolean all_actions = FALSE; gboolean have_stdout = FALSE; pe_working_set_t data_set; const char *xml_file = "-"; const char *quorum = NULL; const char *watchdog = NULL; const char *test_dir = 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; GListPtr node_up = NULL; GListPtr node_down = NULL; GListPtr node_fail = NULL; GListPtr op_inject = NULL; GListPtr ticket_grant = NULL; GListPtr ticket_revoke = NULL; GListPtr ticket_standby = NULL; GListPtr ticket_activate = NULL; xmlNode *input = NULL; crm_log_cli_init("crm_simulate"); crm_set_options(NULL, "datasource operation [additional options]", long_options, "Tool for simulating the cluster's response to events"); if (argc < 2) { crm_help('?', EX_USAGE); } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch (flag) { case 'V': if (have_stdout == FALSE) { /* Redirect stderr to stdout so we can grep the output */ have_stdout = TRUE; close(STDERR_FILENO); dup2(STDOUT_FILENO, STDERR_FILENO); } crm_bump_log_level(argc, argv); break; case '?': case '$': crm_help(flag, EX_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': modified++; bringing_nodes_online = TRUE; node_up = g_list_append(node_up, optarg); break; case 'd': modified++; node_down = g_list_append(node_down, optarg); break; case 'f': modified++; node_fail = g_list_append(node_fail, optarg); break; case 't': use_date = strdup(optarg); break; case 'i': modified++; op_inject = g_list_append(op_inject, optarg); break; case 'F': process = TRUE; simulate = TRUE; op_fail = g_list_append(op_fail, optarg); break; case 'w': modified++; watchdog = optarg; break; case 'q': modified++; quorum = optarg; break; case 'g': modified++; ticket_grant = g_list_append(ticket_grant, optarg); break; case 'r': modified++; ticket_revoke = g_list_append(ticket_revoke, optarg); break; case 'b': modified++; ticket_standby = g_list_append(ticket_standby, optarg); break; case 'e': modified++; ticket_activate = g_list_append(ticket_activate, optarg); break; case 'a': all_actions = TRUE; break; case 's': process = TRUE; show_scores = TRUE; break; case 'U': process = TRUE; show_utilization = TRUE; break; case 'j': print_pending = TRUE; break; case 'S': process = TRUE; simulate = TRUE; break; case 'X': store = TRUE; process = TRUE; simulate = TRUE; break; case 'R': process = TRUE; break; case 'D': process = TRUE; dot_file = optarg; break; case 'G': process = TRUE; graph_file = optarg; break; case 'I': input_file = optarg; break; case 'O': output_file = optarg; break; case 'P': test_dir = optarg; break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', EX_USAGE); } if (test_dir != NULL) { return profile_all(test_dir); } setup_input(xml_file, store ? xml_file : output_file); global_cib = cib_new(); global_cib->cmds->signon(global_cib, crm_system_name, cib_command); set_working_set_defaults(&data_set); if (data_set.now != NULL) { quiet_log(" + Setting effective cluster time: %s", use_date); crm_time_log(LOG_WARNING, "Set fake 'now' to", data_set.now, crm_time_log_date | crm_time_log_timeofday); } rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call | cib_scope_local); CRM_ASSERT(rc == pcmk_ok); data_set.input = input; data_set.now = get_date(); cluster_status(&data_set); if (quiet == FALSE) { int options = print_pending ? pe_print_pending : 0; quiet_log("\nCurrent cluster status:\n"); print_cluster_status(&data_set, options); } if (modified) { quiet_log("Performing requested modifications\n"); - modify_configuration(&data_set, quorum, watchdog, node_up, node_down, node_fail, op_inject, + modify_configuration(&data_set, global_cib, quorum, watchdog, node_up, node_down, node_fail, op_inject, ticket_grant, ticket_revoke, ticket_standby, ticket_activate); rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call); if (rc != pcmk_ok) { fprintf(stderr, "Could not connect to the CIB for input: %s\n", pcmk_strerror(rc)); goto done; } cleanup_alloc_calculations(&data_set); data_set.now = get_date(); data_set.input = input; } if (input_file != NULL) { rc = write_xml_file(input, input_file, FALSE); if (rc < 0) { fprintf(stderr, "Could not create '%s': %s\n", input_file, strerror(errno)); goto done; } } rc = 0; if (process || 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"); } do_calculations(&data_set, input, local_date); input = NULL; /* Don't try and free it twice */ if (graph_file != NULL) { write_xml_file(data_set.graph, graph_file, FALSE); } if (dot_file != NULL) { create_dotfile(&data_set, dot_file, all_actions); } if (quiet == FALSE) { GListPtr gIter = NULL; quiet_log("%sTransition Summary:\n", show_scores || show_utilization || modified ? "\n" : ""); fflush(stdout); for (gIter = data_set.resources; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; LogActions(rsc, &data_set, TRUE); } } } if (simulate) { - rc = run_simulation(&data_set); + rc = run_simulation(&data_set, global_cib, op_fail, quiet); + + if (quiet == FALSE) { + xmlNode *cib_object = NULL; + int rc = global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call | cib_scope_local); + + CRM_ASSERT(rc == pcmk_ok); + quiet_log("\nRevised cluster status:\n"); + cleanup_alloc_calculations(&data_set); + data_set.input = cib_object; + data_set.now = get_date(); + + cluster_status(&data_set); + print_cluster_status(&data_set, 0); + } } done: cleanup_alloc_calculations(&data_set); global_cib->cmds->signoff(global_cib); cib_delete(global_cib); free(use_date); fflush(stderr); if (temp_shadow) { unlink(temp_shadow); free(temp_shadow); } return crm_exit(rc); } diff --git a/tools/fake_transition.c b/tools/fake_transition.c new file mode 100644 index 0000000000..4fbba1260e --- /dev/null +++ b/tools/fake_transition.c @@ -0,0 +1,836 @@ +/* + * 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 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 + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "fake_transition.h" + +static bool fake_quiet = FALSE; +static cib_t *fake_cib = NULL; +static GListPtr fake_resource_list = NULL; +static GListPtr fake_op_fail_list = NULL; + +#define STATUS_PATH_MAX 512 + +#define quiet_log(fmt, args...) do { \ + if(fake_quiet) { \ + crm_trace(fmt, ##args); \ + } else { \ + printf(fmt , ##args); \ + } \ + } while(0) + +#define new_node_template "//"XML_CIB_TAG_NODE"[@uname='%s']" +#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']" */ + + +static void +inject_transient_attr(xmlNode * cib_node, const char *name, const char *value) +{ + xmlNode *attrs = NULL; + xmlNode *container = NULL; + xmlNode *nvp = NULL; + const char *node_uuid = ID(cib_node); + char *nvp_id = crm_concat(name, node_uuid, '-'); + + quiet_log("Injecting attribute %s=%s into %s '%s'", name, value, xmlGetNodePath(cib_node), + ID(cib_node)); + + attrs = first_named_child(cib_node, XML_TAG_TRANSIENT_NODEATTRS); + if (attrs == NULL) { + attrs = create_xml_node(cib_node, XML_TAG_TRANSIENT_NODEATTRS); + crm_xml_add(attrs, XML_ATTR_ID, node_uuid); + } + + container = first_named_child(attrs, XML_TAG_ATTR_SETS); + if (container == NULL) { + container = create_xml_node(attrs, XML_TAG_ATTR_SETS); + crm_xml_add(container, XML_ATTR_ID, node_uuid); + } + + nvp = create_xml_node(container, XML_CIB_TAG_NVPAIR); + crm_xml_add(nvp, XML_ATTR_ID, nvp_id); + crm_xml_add(nvp, XML_NVPAIR_ATTR_NAME, name); + crm_xml_add(nvp, XML_NVPAIR_ATTR_VALUE, value); + + free(nvp_id); +} + +static void +update_failcounts(xmlNode * cib_node, const char *resource, int interval, int rc) +{ + if (rc == 0) { + return; + + } else if (rc == 7 && interval == 0) { + return; + + } else { + char *name = NULL; + char *now = crm_itoa(time(NULL)); + + name = crm_concat("fail-count", resource, '-'); + inject_transient_attr(cib_node, name, "value++"); + + name = crm_concat("last-failure", resource, '-'); + inject_transient_attr(cib_node, name, now); + + free(name); + free(now); + } +} + +static void +create_node_entry(cib_t * cib_conn, const char *node) +{ + int rc = pcmk_ok; + int max = strlen(new_node_template) + strlen(node) + 1; + char *xpath = NULL; + + xpath = calloc(1, max); + + snprintf(xpath, max, new_node_template, node); + rc = cib_conn->cmds->query(cib_conn, xpath, NULL, cib_xpath | cib_sync_call | cib_scope_local); + + if (rc == -ENXIO) { + xmlNode *cib_object = create_xml_node(NULL, XML_CIB_TAG_NODE); + + /* Using node uname as uuid ala corosync/openais */ + crm_xml_add(cib_object, XML_ATTR_ID, node); + crm_xml_add(cib_object, XML_ATTR_UNAME, node); + cib_conn->cmds->create(cib_conn, XML_CIB_TAG_NODES, cib_object, + cib_sync_call | cib_scope_local); + /* Not bothering with subsequent query to see if it exists, + we'll bomb out later in the call to query_node_uuid()... */ + + free_xml(cib_object); + } + + free(xpath); +} + +static lrmd_event_data_t * +create_op(xmlNode * cib_resource, const char *task, int interval, int outcome) +{ + lrmd_event_data_t *op = NULL; + xmlNode *xop = NULL; + + op = calloc(1, sizeof(lrmd_event_data_t)); + + op->rsc_id = strdup(ID(cib_resource)); + op->interval = interval; + op->op_type = strdup(task); + + op->rc = outcome; + op->op_status = 0; + op->params = NULL; /* TODO: Fill me in */ + op->t_run = time(NULL); + op->t_rcchange = op->t_run; + + op->call_id = 0; + for (xop = __xml_first_child(cib_resource); xop != NULL; xop = __xml_next(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, lrmd_event_data_t * op, int target_rc) +{ + return create_operation_update(cib_resource, op, CRM_FEATURE_SET, target_rc, crm_system_name, + LOG_DEBUG_2); +} + +static xmlNode * +inject_node_state(cib_t * cib_conn, const char *node, const char *uuid) +{ + int rc = pcmk_ok; + int max = strlen(rsc_template) + strlen(node) + 1; + char *xpath = NULL; + xmlNode *cib_object = NULL; + + xpath = calloc(1, max); + + /* if (bringing_nodes_online) { */ + create_node_entry(cib_conn, node); + /* } */ + + 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 (cib_object && ID(cib_object) == NULL) { + crm_err("Detected multiple node_state entries for xpath=%s, bailing", xpath); + crm_log_xml_warn(cib_object, "Duplicates"); + crm_exit(ENOTUNIQ); + } + + if (rc == -ENXIO) { + char *found_uuid = NULL; + + if (uuid == NULL) { + query_node_uuid(cib_conn, node, &found_uuid, NULL); + } else { + found_uuid = strdup(uuid); + } + + cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); + crm_xml_add(cib_object, XML_ATTR_UUID, found_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); + free_xml(cib_object); + free(found_uuid); + + rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, + cib_xpath | cib_sync_call | cib_scope_local); + crm_trace("injecting node state for %s. rc is %d", node, rc); + } + + free(xpath); + CRM_ASSERT(rc == pcmk_ok); + return cib_object; +} + +static xmlNode * +modify_node(cib_t * cib_conn, char *node, gboolean up) +{ + xmlNode *cib_node = inject_node_state(cib_conn, node, NULL); + + if (up) { + crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_YES); + crm_xml_add(cib_node, XML_NODE_IS_PEER, ONLINESTATUS); + crm_xml_add(cib_node, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_MEMBER); + crm_xml_add(cib_node, XML_NODE_EXPECTED, CRMD_JOINSTATE_MEMBER); + + } else { + crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_NO); + crm_xml_add(cib_node, XML_NODE_IS_PEER, OFFLINESTATUS); + crm_xml_add(cib_node, XML_NODE_JOIN_STATE, CRMD_JOINSTATE_DOWN); + crm_xml_add(cib_node, XML_NODE_EXPECTED, CRMD_JOINSTATE_DOWN); + } + + crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name); + return cib_node; +} + +static xmlNode * +find_resource_xml(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; + + xpath = calloc(1, max); + + snprintf(xpath, max, rsc_template, node, resource); + match = get_xpath_object(xpath, cib_node, LOG_DEBUG_2); + + free(xpath); + return match; +} + + +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; + char *xpath = NULL; + + cib_resource = find_resource_xml(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, "stonith") + && safe_str_neq(rclass, "heartbeat") + && safe_str_neq(rclass, "service") + && safe_str_neq(rclass, "upstart") + && safe_str_neq(rclass, "systemd") + && 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; + } + + xpath = (char *)xmlGetNodePath(cib_node); + crm_info("Injecting new resource %s into %s '%s'", resource, xpath, ID(cib_node)); + free(xpath); + + 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 int +find_ticket_state(cib_t * the_cib, const char *ticket_id, xmlNode ** ticket_state_xml) +{ + int offset = 0; + static int xpath_max = 1024; + int rc = pcmk_ok; + xmlNode *xml_search = NULL; + + char *xpath_string = NULL; + + CRM_ASSERT(ticket_state_xml != NULL); + *ticket_state_xml = NULL; + + xpath_string = calloc(1, xpath_max); + offset += snprintf(xpath_string + offset, xpath_max - offset, "%s", "/cib/status/tickets"); + + if (ticket_id) { + offset += snprintf(xpath_string + offset, xpath_max - offset, "/%s[@id=\"%s\"]", + XML_CIB_TAG_TICKET_STATE, ticket_id); + } + CRM_LOG_ASSERT(offset > 0); + rc = the_cib->cmds->query(the_cib, xpath_string, &xml_search, + cib_sync_call | cib_scope_local | cib_xpath); + + if (rc != pcmk_ok) { + goto bail; + } + + crm_log_xml_debug(xml_search, "Match"); + if (xml_has_children(xml_search)) { + if (ticket_id) { + fprintf(stdout, "Multiple ticket_states match ticket_id=%s\n", ticket_id); + } + *ticket_state_xml = xml_search; + } else { + *ticket_state_xml = xml_search; + } + + bail: + free(xpath_string); + return rc; +} + +static int +set_ticket_state_attr(const char *ticket_id, const char *attr_name, + const char *attr_value, cib_t * cib, int cib_options) +{ + int rc = pcmk_ok; + xmlNode *xml_top = NULL; + xmlNode *ticket_state_xml = NULL; + + rc = find_ticket_state(cib, ticket_id, &ticket_state_xml); + if (rc == pcmk_ok) { + crm_debug("Found a match state for ticket: id=%s", ticket_id); + xml_top = ticket_state_xml; + + } else if (rc != -ENXIO) { + return rc; + + } else { + xmlNode *xml_obj = NULL; + + xml_top = create_xml_node(NULL, XML_CIB_TAG_STATUS); + xml_obj = create_xml_node(xml_top, XML_CIB_TAG_TICKETS); + ticket_state_xml = create_xml_node(xml_obj, XML_CIB_TAG_TICKET_STATE); + crm_xml_add(ticket_state_xml, XML_ATTR_ID, ticket_id); + } + + crm_xml_add(ticket_state_xml, attr_name, attr_value); + + crm_log_xml_debug(xml_top, "Update"); + + rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, xml_top, cib_options); + + free_xml(xml_top); + + return rc; +} + +void +modify_configuration(pe_working_set_t * data_set, cib_t *cib, + const char *quorum, const char *watchdog, GListPtr node_up, GListPtr node_down, GListPtr node_fail, + GListPtr op_inject, GListPtr ticket_grant, GListPtr ticket_revoke, + GListPtr ticket_standby, GListPtr ticket_activate) +{ + int rc = pcmk_ok; + GListPtr gIter = NULL; + + xmlNode *cib_op = NULL; + xmlNode *cib_node = NULL; + xmlNode *cib_resource = NULL; + + lrmd_event_data_t *op = NULL; + + if (quorum) { + xmlNode *top = create_xml_node(NULL, XML_TAG_CIB); + + 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 = cib->cmds->modify(cib, NULL, top, cib_sync_call | cib_scope_local); + CRM_ASSERT(rc == pcmk_ok); + } + + if (watchdog) { + quiet_log(" + Setting watchdog: %s\n", watchdog); + + rc = update_attr_delegate(cib, cib_sync_call | cib_scope_local, + XML_CIB_TAG_CRMCONFIG, NULL, NULL, NULL, NULL, + XML_ATTR_HAVE_WATCHDOG, watchdog, FALSE, NULL, NULL); + + CRM_ASSERT(rc == pcmk_ok); + } + + for (gIter = node_up; gIter != NULL; gIter = gIter->next) { + char *node = (char *)gIter->data; + + quiet_log(" + Bringing node %s online\n", node); + cib_node = modify_node(cib, node, TRUE); + CRM_ASSERT(cib_node != NULL); + + rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + cib_sync_call | cib_scope_local); + CRM_ASSERT(rc == pcmk_ok); + free_xml(cib_node); + } + + for (gIter = node_down; gIter != NULL; gIter = gIter->next) { + char xpath[STATUS_PATH_MAX]; + char *node = (char *)gIter->data; + + quiet_log(" + Taking node %s offline\n", node); + cib_node = modify_node(cib, node, FALSE); + CRM_ASSERT(cib_node != NULL); + + rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + cib_sync_call | cib_scope_local); + CRM_ASSERT(rc == pcmk_ok); + free_xml(cib_node); + + snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", node, XML_CIB_TAG_LRM); + cib->cmds->delete(cib, xpath, NULL, + cib_xpath | cib_sync_call | cib_scope_local); + + snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", node, + XML_TAG_TRANSIENT_NODEATTRS); + cib->cmds->delete(cib, xpath, NULL, + cib_xpath | cib_sync_call | cib_scope_local); + + } + + for (gIter = node_fail; gIter != NULL; gIter = gIter->next) { + char *node = (char *)gIter->data; + + quiet_log(" + Failing node %s\n", node); + cib_node = modify_node(cib, node, TRUE); + crm_xml_add(cib_node, XML_NODE_IN_CLUSTER, XML_BOOLEAN_NO); + CRM_ASSERT(cib_node != NULL); + + rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + cib_sync_call | cib_scope_local); + CRM_ASSERT(rc == pcmk_ok); + free_xml(cib_node); + } + + for (gIter = ticket_grant; gIter != NULL; gIter = gIter->next) { + char *ticket_id = (char *)gIter->data; + + quiet_log(" + Granting ticket %s\n", ticket_id); + rc = set_ticket_state_attr(ticket_id, "granted", "true", + cib, cib_sync_call | cib_scope_local); + + CRM_ASSERT(rc == pcmk_ok); + } + + for (gIter = ticket_revoke; gIter != NULL; gIter = gIter->next) { + char *ticket_id = (char *)gIter->data; + + quiet_log(" + Revoking ticket %s\n", ticket_id); + rc = set_ticket_state_attr(ticket_id, "granted", "false", + cib, cib_sync_call | cib_scope_local); + + CRM_ASSERT(rc == pcmk_ok); + } + + for (gIter = ticket_standby; gIter != NULL; gIter = gIter->next) { + char *ticket_id = (char *)gIter->data; + + quiet_log(" + Making ticket %s standby\n", ticket_id); + rc = set_ticket_state_attr(ticket_id, "standby", "true", + cib, cib_sync_call | cib_scope_local); + + CRM_ASSERT(rc == pcmk_ok); + } + + for (gIter = ticket_activate; gIter != NULL; gIter = gIter->next) { + char *ticket_id = (char *)gIter->data; + + quiet_log(" + Activating ticket %s\n", ticket_id); + rc = set_ticket_state_attr(ticket_id, "standby", "false", + cib, cib_sync_call | cib_scope_local); + + CRM_ASSERT(rc == pcmk_ok); + } + + for (gIter = op_inject; gIter != NULL; gIter = gIter->next) { + char *spec = (char *)gIter->data; + + 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; + + quiet_log(" + Injecting %s into the configuration\n", spec); + + key = calloc(1, strlen(spec) + 1); + node = calloc(1, strlen(spec) + 1); + 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); + if (rsc == NULL) { + fprintf(stderr, " - Invalid resource name: %s\n", resource); + } else { + 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_state(cib, node, NULL); + CRM_ASSERT(cib_node != NULL); + + update_failcounts(cib_node, resource, interval, outcome); + + 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); + lrmd_free_event(op); + + rc = cib->cmds->modify(cib, XML_CIB_TAG_STATUS, cib_node, + cib_sync_call | cib_scope_local); + CRM_ASSERT(rc == pcmk_ok); + } + free(task); + free(node); + free(key); + } +} + +static gboolean +exec_pseudo_action(crm_graph_t * graph, crm_action_t * action) +{ + const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); + const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK_KEY); + + action->confirmed = TRUE; + + quiet_log(" * Pseudo action: %s%s%s\n", task, node ? " on " : "", node ? node : ""); + update_graph(graph, action); + return TRUE; +} + +static gboolean +exec_rsc_action(crm_graph_t * graph, crm_action_t * action) +{ + int rc = 0; + GListPtr gIter = NULL; + lrmd_event_data_t *op = NULL; + int target_outcome = 0; + gboolean uname_is_uuid = FALSE; + + const char *rtype = NULL; + const char *rclass = NULL; + const char *resource = NULL; + const char *rprovider = NULL; + const char *operation = crm_element_value(action->xml, "operation"); + const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC); + + xmlNode *cib_node = 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); + char *uuid = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET_UUID); + const char *router_node = crm_element_value(action->xml, XML_LRM_ATTR_ROUTER_NODE); + + if (safe_str_eq(operation, CRM_OP_PROBED) + || safe_str_eq(operation, CRM_OP_REPROBE)) { + crm_info("Skipping %s op for %s\n", operation, node); + goto done; + } + + if (action_rsc == NULL) { + crm_log_xml_err(action->xml, "Bad"); + free(node); free(uuid); + return FALSE; + } + + /* Look for the preferred name + * If not found, try the expected 'local' name + * If not found use the preferred name anyway + */ + resource = crm_element_value(action_rsc, XML_ATTR_ID); + if (pe_find_resource(fake_resource_list, resource) == NULL) { + const char *longname = crm_element_value(action_rsc, XML_ATTR_ID_LONG); + + if (pe_find_resource(fake_resource_list, longname)) { + resource = longname; + } + } + + if (safe_str_eq(operation, "delete")) { + quiet_log(" * Resource action: %-15s delete on %s\n", resource, node); + goto done; + } + + 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(fake_cib->cmds->query(fake_cib, NULL, NULL, cib_sync_call | cib_scope_local) == + pcmk_ok); + + if (router_node) { + uname_is_uuid = TRUE; + } + + cib_node = inject_node_state(fake_cib, node, uname_is_uuid ? node : uuid); + 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); + if (op->interval) { + quiet_log(" * Resource action: %-15s %s=%d on %s\n", resource, op->op_type, op->interval, + node); + } else { + quiet_log(" * Resource action: %-15s %s on %s\n", resource, op->op_type, node); + } + + for (gIter = fake_op_fail_list; gIter != NULL; gIter = gIter->next) { + char *spec = (char *)gIter->data; + char *key = NULL; + + key = calloc(1, 1 + strlen(spec)); + snprintf(key, strlen(spec), "%s_%s_%d@%s=", resource, op->op_type, op->interval, node); + + if (strncasecmp(key, spec, strlen(key)) == 0) { + sscanf(spec, "%*[^=]=%d", (int *)&op->rc); + + action->failed = TRUE; + graph->abort_priority = INFINITY; + printf("\tPretending action %d failed with rc=%d\n", action->id, op->rc); + update_failcounts(cib_node, resource, op->interval, op->rc); + free(key); + break; + } + free(key); + } + + inject_op(cib_resource, op, target_outcome); + lrmd_free_event(op); + + rc = fake_cib->cmds->modify(fake_cib, XML_CIB_TAG_STATUS, cib_node, + cib_sync_call | cib_scope_local); + CRM_ASSERT(rc == pcmk_ok); + + done: + free(node); free(uuid); + free_xml(cib_node); + action->confirmed = TRUE; + update_graph(graph, action); + return TRUE; +} + +static gboolean +exec_crmd_action(crm_graph_t * graph, crm_action_t * action) +{ + const char *node = crm_element_value(action->xml, XML_LRM_ATTR_TARGET); + const char *task = crm_element_value(action->xml, XML_LRM_ATTR_TASK); + xmlNode *rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); + + action->confirmed = TRUE; + + if(rsc) { + quiet_log(" * Cluster action: %s for %s on %s\n", task, ID(rsc), node); + } else { + quiet_log(" * Cluster action: %s on %s\n", task, node); + } + update_graph(graph, action); + return TRUE; +} + +static gboolean +exec_stonith_action(crm_graph_t * graph, crm_action_t * action) +{ + const char *op = crm_meta_value(action->params, "stonith_action"); + char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); + + quiet_log(" * Fencing %s (%s)\n", target, op); + if(safe_str_neq(op, "on")) { + int rc = 0; + char xpath[STATUS_PATH_MAX]; + xmlNode *cib_node = modify_node(fake_cib, target, FALSE); + + crm_xml_add(cib_node, XML_ATTR_ORIGIN, __FUNCTION__); + CRM_ASSERT(cib_node != NULL); + + rc = fake_cib->cmds->replace(fake_cib, XML_CIB_TAG_STATUS, cib_node, + cib_sync_call | cib_scope_local); + CRM_ASSERT(rc == pcmk_ok); + + snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM); + fake_cib->cmds->delete(fake_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); + fake_cib->cmds->delete(fake_cib, xpath, NULL, + cib_xpath | cib_sync_call | cib_scope_local); + + free_xml(cib_node); + } + + action->confirmed = TRUE; + update_graph(graph, action); + free(target); + return TRUE; +} + +int +run_simulation(pe_working_set_t * data_set, cib_t *cib, GListPtr op_fail_list, bool quiet) +{ + 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, + }; + + fake_cib = cib; + fake_quiet = quiet; + fake_op_fail_list = op_fail_list; + + quiet_log("\nExecuting cluster transition:\n"); + + set_graph_functions(&exec_fns); + transition = unpack_graph(data_set->graph, crm_system_name); + print_graph(LOG_DEBUG, transition); + + fake_resource_list = data_set->resources; + do { + graph_rc = run_graph(transition); + + } while (graph_rc == transition_active); + fake_resource_list = NULL; + + if (graph_rc != transition_complete) { + fprintf(stdout, "Transition failed: %s\n", transition_status(graph_rc)); + print_graph(LOG_ERR, transition); + } + destroy_graph(transition); + if (graph_rc != transition_complete) { + fprintf(stdout, "An invalid transition was produced\n"); + } + + if (graph_rc != transition_complete) { + return graph_rc; + } + return 0; +} diff --git a/tools/fake_transition.h b/tools/fake_transition.h new file mode 100644 index 0000000000..2ee7af9a9f --- /dev/null +++ b/tools/fake_transition.h @@ -0,0 +1,8 @@ +void modify_configuration( + pe_working_set_t * data_set, cib_t *cib, + const char *quorum, const char *watchdog, GListPtr node_up, GListPtr node_down, GListPtr node_fail, + GListPtr op_inject, GListPtr ticket_grant, GListPtr ticket_revoke, + GListPtr ticket_standby, GListPtr ticket_activate); + +int run_simulation(pe_working_set_t * data_set, cib_t *cib, GListPtr op_fail_list, bool quiet); +