diff --git a/crm/tengine/unpack.c b/crm/tengine/unpack.c new file mode 100644 index 0000000000..ac2c396cb4 --- /dev/null +++ b/crm/tengine/unpack.c @@ -0,0 +1,438 @@ +/* $Id: unpack.c,v 1.1 2004/09/15 09:22:57 andrew Exp $ */ +/* + * 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.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +gboolean process_te_message(xmlNodePtr msg, IPC_Channel *sender); +action_t* unpack_action(xmlNodePtr xml_action); +xmlNodePtr create_shutdown_event(const char *node, int op_status); + +gboolean +unpack_graph(xmlNodePtr xml_graph) +{ +/* + + + + + id = num_synapses++; + new_synapse->complete = FALSE; + new_synapse->actions = NULL; + new_synapse->inputs = NULL; + + graph = g_list_append(graph, new_synapse); + + xml_child_iter( + synapse, actions, "action_set", + + xml_child_iter( + actions, action, NULL, + + action_t *new_action = unpack_action(action); + num_actions++; + + if(new_action == NULL) { + action = action->next; + break; + } + new_synapse->actions = g_list_append( + new_synapse->actions, + new_action); + ); + + ); + + xml_child_iter( + synapse, inputs, "inputs", + + xml_child_iter( + inputs, trigger, NULL, + + xml_child_iter( + trigger, input, NULL, + + action_t *new_input = + unpack_action(input); + + if(new_input == NULL) { + input = input->next; + break; + } + + new_synapse->inputs = g_list_append( + new_synapse->inputs, + new_input); + ); + ); + ); + ); + + crm_info("Unpacked %d actions in %d synapses", + num_actions, num_synapses); + + if(num_actions > 0) { + return TRUE; + } else { + /* indicate to caller that there's nothing to do */ + return FALSE; + } + +} + +action_t* +unpack_action(xmlNodePtr xml_action) +{ + const char *tmp = xmlGetProp(xml_action, "id"); + action_t *action = NULL; + xmlNodePtr action_copy = NULL; + + if(tmp == NULL) { + crm_err("Actions must have an id!"); + crm_xml_devel(xml_action, "Action with missing id"); + return NULL; + } + + action_copy = copy_xml_node_recursive(xml_action); + action = crm_malloc(sizeof(action_t)); + + action->id = atoi(tmp); + action->timeout = 0; + action->timer_id = -1; + action->invoked = FALSE; + action->complete = FALSE; + action->can_fail = FALSE; + action->type = action_type_rsc; + action->xml = action_copy; + + if(safe_str_eq(action_copy->name, "rsc_op")) { + action->type = action_type_rsc; + + } else if(safe_str_eq(action_copy->name, "pseudo_event")) { + action->type = action_type_pseudo; + + } else if(safe_str_eq(action_copy->name, "crm_event")) { + action->type = action_type_crm; + + } + + tmp = xmlGetProp(action_copy, "timeout"); + if(tmp != NULL) { + action->timeout = atoi(tmp); + } + + tmp = xmlGetProp(action_copy, "can_fail"); + if(safe_str_eq(tmp, "true")) { + action->can_fail = TRUE; + } + + return action; +} + + +gboolean +extract_event(xmlNodePtr msg) +{ + gboolean abort = FALSE; + xmlNodePtr iter = NULL; + xmlNodePtr cib = NULL; + const char *section = NULL; + const char *event_node = NULL; + +/* +[cib fragment] +... + + + + + +*/ + + crm_trace("Extracting event"); + + iter = find_xml_node(msg, XML_TAG_FRAGMENT); + section = xmlGetProp(iter, XML_ATTR_SECTION); + + if(safe_str_eq(section, XML_CIB_TAG_CRMCONFIG)) { + /* ignore - for the moment */ + crm_debug("Ignoring changes to the %s section", + XML_CIB_TAG_CRMCONFIG); + return TRUE; + + } else if(safe_str_neq(section, XML_CIB_TAG_STATUS)) { + /* these too are never expected */ + crm_debug("Ignoring changes outside the %s section", + XML_CIB_TAG_STATUS); + return FALSE; + } + + cib = find_xml_node(iter, XML_TAG_CIB); + iter = get_object_root(XML_CIB_TAG_STATUS, cib); + if(iter != NULL) { + iter = iter->children; + } else { + crm_xml_warn(cib, XML_CIB_TAG_STATUS " section missing?"); + } + + + while(abort == FALSE && iter != NULL) { + xmlNodePtr node_state = iter; + xmlNodePtr child = iter->children; + const char *state = xmlGetProp( + node_state, XML_CIB_ATTR_CRMDSTATE); + iter = iter->next; + + crm_xml_devel(node_state,"Processing"); + + if(xmlGetProp(node_state, XML_CIB_ATTR_SHUTDOWN) != NULL) { + crm_trace("Aborting on %s attribute", + XML_CIB_ATTR_SHUTDOWN); + abort = TRUE; + + } else if(xmlGetProp(node_state, XML_CIB_ATTR_STONITH) != NULL) { + /* node marked for STONITH + * possibly by us when a shutdown timmed out + */ + crm_trace("Checking for STONITH"); + event_node = xmlGetProp(node_state, XML_ATTR_UNAME); + + xmlNodePtr shutdown = create_shutdown_event( + event_node, LRM_OP_TIMEOUT); + + process_graph_event(shutdown); + + free_xml(shutdown); + + } else if(state != NULL && child == NULL) { + /* simple node state update... + * possibly from a shutdown we requested + */ + crm_trace("Processing simple state update"); + if(safe_str_neq(state, OFFLINESTATUS)) { + /* always recompute */ + abort = TRUE; + continue; + } + + event_node = xmlGetProp(node_state, XML_ATTR_UNAME); + xmlNodePtr shutdown = create_shutdown_event( + event_node, LRM_OP_DONE); + + process_graph_event(shutdown); + + free_xml(shutdown); + + } else if(state == NULL && child != NULL) { + /* LRM resource update... + */ + crm_trace("Processing LRM resource update"); + child = find_xml_node(node_state, XML_CIB_TAG_LRM); + child = find_xml_node(child, XML_LRM_TAG_RESOURCES); + + if(child != NULL) { + child = child->children; + } else { + abort = TRUE; + } + + event_node = xmlGetProp(node_state, XML_ATTR_UNAME); + while(abort == FALSE && child != NULL) { + process_graph_event(child); + child = child->next; + } + } else if(state != NULL && child != NULL) { + /* this is a complex event and could not be completely + * due to any request we made + */ + crm_trace("Aborting on complex update"); + abort = TRUE; + + } else { + /* ignore */ + crm_err("Ignoring message"); + } + } + + return !abort; +} + +gboolean +process_te_message(xmlNodePtr msg, IPC_Channel *sender) +{ + const char *op = get_xml_attr (msg, XML_TAG_OPTIONS, + XML_ATTR_OP, FALSE); + + const char *sys_to = xmlGetProp(msg, XML_ATTR_SYSTO); + const char *ref = xmlGetProp(msg, XML_ATTR_REFERENCE); + xmlNodePtr graph = NULL; + + crm_debug("Recieved %s (%s) message", op, ref); + +#ifdef MSG_LOG + + struct stat buf; + if(stat(DEVEL_DIR, &buf) != 0) { + cl_perror("Stat of %s failed... exiting", DEVEL_DIR); + exit(100); + } + + if(msg_te_strm == NULL) { + msg_te_strm = fopen(DEVEL_DIR"/te.log", "w"); + } + char *xml = dump_xml_formatted(msg); + fprintf(msg_te_strm, "[Input %s]\t%s\n", + op, xml); + fflush(msg_te_strm); + crm_free(xml); +#endif + + if(safe_str_eq(xmlGetProp(msg, XML_ATTR_MSGTYPE), XML_ATTR_RESPONSE) + && safe_str_neq(op, CRM_OP_EVENTCC)) { +#ifdef MSG_LOG + fprintf(msg_te_strm, "[Result ]\tDiscarded\n"); + fflush(msg_te_strm); +#endif + crm_info("Message was a response not a request. Discarding"); + return TRUE; + } + + crm_debug("Processing %s (%s) message", op, ref); + + if(op == NULL){ + /* error */ + } else if(strcmp(op, CRM_OP_HELLO) == 0) { + /* ignore */ + + } else if(sys_to == NULL || strcmp(sys_to, CRM_SYSTEM_TENGINE) != 0) { + crm_verbose("Bad sys-to %s", sys_to); + return FALSE; + + } else if(strcmp(op, CRM_OP_TRANSITION) == 0) { + + crm_trace("Initializing graph..."); + initialize_graph(); + + graph = find_xml_node(msg, "transition_graph"); + crm_trace("Unpacking graph..."); + unpack_graph(graph); + crm_trace("Initiating transition..."); + + in_transition = TRUE; + + if(initiate_transition() == FALSE) { + /* nothing to be done.. means we're done. */ + crm_info("No actions to be taken..." + " transition compelte."); + } + crm_trace("Processing complete..."); + + } else if(strcmp(op, CRM_OP_ABORT) == 0) { + initialize_graph(); + + } else if(strcmp(op, CRM_OP_QUIT) == 0) { + crm_err("Received quit message, terminating"); + exit(0); + + } else if(in_transition == FALSE) { + crm_info("Received event_cc while not in a transition..." + " Poking the Policy Engine"); + send_abort("Initiate a transition", NULL); + + } else if(strcmp(op, CRM_OP_EVENTCC) == 0) { + const char *true_op = get_xml_attr (msg, XML_TAG_OPTIONS, + XML_ATTR_TRUEOP, TRUE); + crm_trace("Processing %s...", CRM_OP_EVENTCC); + if(true_op == NULL) { + crm_err( + "Illegal update," + " the original operation must be specified"); + send_abort("Illegal update", msg); + + } else if(strcmp(true_op, CRM_OP_CREATE) == 0 + || strcmp(true_op, CRM_OP_DELETE) == 0 + || strcmp(true_op, CRM_OP_REPLACE) == 0 + || strcmp(true_op, CRM_OP_WELCOME) == 0 + || strcmp(true_op, CRM_OP_SHUTDOWN_REQ) == 0 + || strcmp(true_op, CRM_OP_ERASE) == 0) { + + /* these are always unexpected, trigger the PE */ + send_abort("Config update", msg); + + } else if(strcmp(true_op, CRM_OP_UPDATE) == 0) { + /* this may not be un-expected */ +// if( + extract_event(msg); + //== FALSE){ +// send_abort(msg); +// } + + } else { + crm_err( + "Did not expect copy of action %s", op); + } + + } + + crm_debug("finished processing message"); + print_state(FALSE); + + return TRUE; +} + + +xmlNodePtr +create_shutdown_event(const char *node, int op_status) +{ + xmlNodePtr event = create_xml_node(NULL, XML_CIB_TAG_STATE); + char *code = crm_itoa(op_status); + + set_xml_property_copy(event, XML_LRM_ATTR_TARGET, node); +// event_rsc = set_xml_property_copy(event, XML_ATTR_ID); + set_xml_property_copy(event, XML_LRM_ATTR_RC, "0"); + set_xml_property_copy( + event, XML_LRM_ATTR_LASTOP, XML_CIB_ATTR_SHUTDOWN); + set_xml_property_copy( + event, XML_LRM_ATTR_RSCSTATE, CRMD_RSCSTATE_GENERIC_OK); + set_xml_property_copy(event, XML_LRM_ATTR_OPSTATUS, code); + + crm_free(code); + return event; +} diff --git a/crm/tengine/utils.c b/crm/tengine/utils.c new file mode 100644 index 0000000000..a415bb7d6c --- /dev/null +++ b/crm/tengine/utils.c @@ -0,0 +1,362 @@ +/* $Id: utils.c,v 1.1 2004/09/15 09:22:57 andrew Exp $ */ +/* + * 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.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +FILE *msg_te_strm = NULL; + +void print_input(const char *prefix, action_t *input, gboolean to_file); +void print_action(const char *prefix, action_t *action, gboolean to_file); + +void +send_abort(const char *text, xmlNodePtr msg) +{ + xmlNodePtr options = create_xml_node(NULL, XML_TAG_OPTIONS); + + crm_info("Sending \"abort\" message... details follow"); + crm_xml_info(msg, text); + +#ifdef MSG_LOG + fprintf(msg_te_strm, "[Result ]\tTransition aborted\n"); + fflush(msg_te_strm); +#endif +#ifdef TESTING + print_state(TRUE); + g_main_quit(mainloop); + return; +#endif + set_xml_property_copy(options, XML_ATTR_OP, CRM_OP_TEABORT); + + send_ipc_request(crm_ch, options, NULL, + NULL, CRM_SYSTEM_DC, CRM_SYSTEM_TENGINE, + NULL, NULL); + + free_xml(options); +} + +void +send_success(const char *text) +{ + xmlNodePtr options = create_xml_node(NULL, XML_TAG_OPTIONS); + + if(in_transition == FALSE) { + crm_warn("Not in transition, not sending message"); + return; + } + in_transition = FALSE; + + crm_info("Sending \"complete\" message: %s", text); + +#ifdef MSG_LOG + if(msg_te_strm != NULL) { + fprintf(msg_te_strm, "[Result ]\tTransition complete\n"); + fflush(msg_te_strm); + } +#endif +#ifdef TESTING + print_state(TRUE); + g_main_quit(mainloop); + return; +#endif + + set_xml_property_copy(options, XML_ATTR_OP, CRM_OP_TECOMPLETE); + + send_ipc_request(crm_ch, options, NULL, + NULL, CRM_SYSTEM_DC, CRM_SYSTEM_TENGINE, + NULL, NULL); + + free_xml(options); +} + +void +print_state(gboolean to_file) +{ + int lpc = 0; + int lpc2 = 0; + FILE *output = msg_te_strm; +#ifdef TESTING + output = stderr; +#endif + if(to_file) { + fprintf(output, "Start Transitioner state\n"); + } + crm_debug("#!!#!!# Start Transitioner state"); + if(graph == NULL) { + crm_debug("\tEmpty transition graph"); + crm_debug("#!!#!!# End Transitioner state"); + if(to_file) { + fprintf(output, + "\tEmpty transition graph\n" + "End Transitioner state\n"); + } + return; + } + + slist_iter( + synapse, synapse_t, graph, lpc, + + crm_debug("Synapse %d %s", + synapse->id, + synapse->complete?"has completed":"is pending"); + + if(to_file) { + fprintf(output, "Synapse %d %s\n", + synapse->id, + synapse->complete?"has completed":"is pending"); + } + if(synapse->complete == FALSE) { + slist_iter( + input, action_t, synapse->inputs, lpc2, + + print_input("\t", input, to_file); + + ); + } + + slist_iter( + action, action_t, synapse->actions, lpc2, + + print_action("\t", action, to_file); + + ); + ); + + crm_debug("#!!#!!# End Transitioner state"); + if(to_file) { + fprintf(output, "End Transitioner state\n"); + } +} + +void +print_input(const char *prefix, action_t *input, gboolean to_file) +{ + FILE *output = msg_te_strm; +#ifdef TESTING + output = stderr; +#endif + crm_debug("%s[Input %d] %s (%d)", + prefix, + input->id, + input->complete?"Satisfied":"Pending", + input->type); + + crm_xml_trace(input->xml, "\t Raw input"); + + if(to_file) { + fprintf(output, + "%s[Input %d] %s (%d)\n", + prefix, + input->id, + input->complete?"Satisfied":"Pending", + input->type); + + fflush(msg_te_strm); + } + +} + +void +print_action(const char *prefix, action_t *action, gboolean to_file) +{ + FILE *output = msg_te_strm; +#ifdef TESTING + output = stderr; +#endif + crm_debug("%s[Action %d] %s (%d - %s fail)", + prefix, + action->id, + action->complete?"Completed": + action->invoked?"In-flight":"Pending", + action->type, + action->can_fail?"can":"cannot"); + + crm_debug("%s timeout=%d, timer=%d", + prefix, + action->timeout, + action->timer_id); + + crm_xml_trace(action->xml, "\t Raw action"); + + if(to_file) { + fprintf(output, + "%s[Action %d] %s (%d - %s fail)\n", + prefix, + action->id, + action->complete?"Completed": + action->invoked?"In-flight":"Pending", + action->type, + action->can_fail?"can":"cannot"); + + fflush(msg_te_strm); + } +} + +gboolean +timer_callback(gpointer data) +{ + if(data == NULL) { + /* global timeout - abort the transition */ + crm_info("Transition timeout reached..." + " marking transition complete."); + crm_warn("Some actions may not have been executed."); + + send_success("timeout"); + return TRUE; + + } else { + /* fail the action + * - which may or may not abort the transition + */ + action_t *action = (action_t*)data; + action->timer_id = -1; + + return do_update_cib(action->xml, LRM_OP_TIMEOUT); + } +} + +gboolean +do_update_cib(xmlNodePtr xml_action, int status) +{ + char *code; + char since_epoch[64]; + xmlNodePtr fragment = NULL; + xmlNodePtr options = NULL; + xmlNodePtr state = NULL; + xmlNodePtr rsc = NULL; + + const char *sys_to = CRM_SYSTEM_DCIB; + const char *task = xmlGetProp(xml_action, XML_LRM_ATTR_TASK); + const char *rsc_id = xmlGetProp(xml_action, XML_LRM_ATTR_RSCID); + const char *target = xmlGetProp(xml_action, XML_LRM_ATTR_TARGET); + const char *target_uuid = + xmlGetProp(xml_action, XML_LRM_ATTR_TARGET_UUID); + + if(status == LRM_OP_TIMEOUT) { + if(xmlGetProp(xml_action, XML_LRM_ATTR_RSCID) != NULL) { + crm_warn("%s: %s %s on %s timed out", + xml_action->name, task, rsc_id, target); + } else { + crm_warn("%s: %s on %s timed out", + xml_action->name, task, target); + } + } + +/* + update the CIB + + + + + +*/ + + fragment = NULL; + options = create_xml_node(NULL, XML_TAG_OPTIONS); + state = create_xml_node(NULL, XML_CIB_TAG_STATE); + +#ifdef TESTING + + /* turn the "pending" notification into a "op completed" notification + * when testing... exercises more code this way. + */ + if(status == -1) { + status = 0; + } + sys_to = CRM_SYSTEM_TENGINE; + set_xml_property_copy(options, XML_ATTR_OP, CRM_OP_EVENTCC); + set_xml_property_copy(options, XML_ATTR_TRUEOP, CRM_OP_UPDATE); +#else + set_xml_property_copy(options, XML_ATTR_OP, CRM_OP_UPDATE); +#endif + set_xml_property_copy(state, XML_ATTR_UUID, target_uuid); + set_xml_property_copy(state, XML_ATTR_UNAME, target); + + if(status != -1 && (safe_str_eq(task, "shutdown_crm"))) { + sprintf(since_epoch, "%ld", (unsigned long)time(NULL)); + set_xml_property_copy(rsc, "stonith", since_epoch); + + } else { + code = crm_itoa(status); + + rsc = create_xml_node(state, "lrm"); + rsc = create_xml_node(rsc, "lrm_resources"); + rsc = create_xml_node(rsc, "lrm_resource"); + + set_xml_property_copy(rsc, XML_ATTR_ID, rsc_id); + set_xml_property_copy(rsc, XML_LRM_ATTR_TARGET, target); + set_xml_property_copy( + rsc, XML_LRM_ATTR_TARGET_UUID, target_uuid); + + if(safe_str_eq(CRMD_RSCSTATE_START, task)) { + set_xml_property_copy( + rsc, XML_LRM_ATTR_RSCSTATE, + CRMD_RSCSTATE_START_PENDING); + + } else if(safe_str_eq(CRMD_RSCSTATE_STOP, task)) { + set_xml_property_copy( + rsc, XML_LRM_ATTR_RSCSTATE, + CRMD_RSCSTATE_STOP_PENDING); + + } else { + crm_warn("Using status \"pending\" for op \"%s\"" + "... this is still in the experimental stage.", + task); + set_xml_property_copy( + rsc, XML_LRM_ATTR_RSCSTATE, + CRMD_RSCSTATE_GENERIC_PENDING); + } + + set_xml_property_copy(rsc, XML_LRM_ATTR_OPSTATUS, code); + set_xml_property_copy(rsc, XML_LRM_ATTR_RC, code); + set_xml_property_copy(rsc, XML_LRM_ATTR_LASTOP, task); + + crm_free(code); + } + + fragment = create_cib_fragment(state, NULL); + +#ifdef MSG_LOG + fprintf(msg_te_strm, + "[Result ]\tUpdate CIB with \"%s\" (%s): %s %s on %s\n", + status<0?"new action":"timeout", + xml_action->name, task, rsc_id, target); + fprintf(msg_te_strm, "[Sent ]\t%s\n", + dump_xml_formatted(fragment)); + fflush(msg_te_strm); +#endif + + send_ipc_request(crm_ch, options, fragment, + NULL, sys_to, CRM_SYSTEM_TENGINE, + NULL, NULL); + + free_xml(fragment); + free_xml(options); + free_xml(state); + + return TRUE; +} +