diff --git a/crm/pengine/graph.c b/crm/pengine/graph.c index ecd84543b8..b5f47677ac 100644 --- a/crm/pengine/graph.c +++ b/crm/pengine/graph.c @@ -1,580 +1,593 @@ /* * 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 update_action(action_t *action); gboolean update_action_states(GListPtr actions) { crm_debug_2("Updating %d actions", g_list_length(actions)); slist_iter( action, action_t, actions, lpc, update_action(action); ); return TRUE; } gboolean update_action(action_t *action) { int local_type = 0; int log_level = LOG_DEBUG_2; gboolean changed = FALSE; - do_crm_log(log_level, "Processing action %s: %s", - action->uuid, action->optional?"optional":"required"); + do_crm_log(log_level, "Processing action %s: %s %s %s", + action->uuid, + action->optional?"optional":"required", + action->runnable?"runnable":"unrunnable", + action->pseudo?"pseudo":action->task); slist_iter( other, action_wrapper_t, action->actions_before, lpc, gboolean other_changed = FALSE; node_t *node = other->action->node; - do_crm_log(log_level, " Checking action %s: %s 0x%.6x", + do_crm_log(log_level, " Checking action %s: %s %s %s (flags=0x%.6x)", other->action->uuid, other->action->optional?"optional":"required", + other->action->runnable?"runnable":"unrunnable", + other->action->pseudo?"pseudo":other->action->task, other->type); local_type = other->type; - + if(local_type & pe_order_restart && other->action->pseudo == FALSE && node != NULL && node->details->online) { local_type |= pe_order_implies_left; do_crm_log(log_level,"Upgrading restart constraint to implies_left"); if(other->action->optional && other->action->runnable && action->runnable == FALSE) { do_crm_log(log_level-1, " * Marking action %s manditory because %s is unrunnable", other->action->uuid, action->uuid); other->action->optional = FALSE; other_changed = TRUE; } } if((local_type & pe_order_runnable_left) && other->action->runnable == FALSE) { if(other->action->implied_by_stonith) { do_crm_log(log_level, "Ignoring un-runnable - implied_by_stonith"); } else if(action->runnable == FALSE) { do_crm_log(log_level+1, "Already un-runnable"); } else { action->runnable = FALSE; do_crm_log(log_level-1, " * Marking action %s un-runnable because of %s", action->uuid, other->action->uuid); changed = TRUE; } } if((local_type & pe_order_runnable_right) && action->runnable == FALSE) { if(action->pseudo) { do_crm_log(log_level, "Ignoring un-runnable - pseudo"); } else if(other->action->runnable == FALSE) { do_crm_log(log_level+1, "Already un-runnable"); } else { other->action->runnable = FALSE; do_crm_log(log_level-1, " * Marking action %s un-runnable because of %s", other->action->uuid, action->uuid); other_changed = TRUE; } } if(local_type & pe_order_implies_left) { if(other->action->optional == FALSE) { /* nothing to do */ do_crm_log(log_level+1, " Ignoring implies left - redundant"); } else if(safe_str_eq(other->action->task, CRMD_ACTION_STOP) && other->action->rsc->fns->state( other->action->rsc, TRUE) == RSC_ROLE_STOPPED) { do_crm_log(log_level-1, " Ignoring implies left - %s already stopped", other->action->rsc->id); } else if(action->optional == FALSE) { other->action->optional = FALSE; do_crm_log(log_level-1, " * (implies left) Marking action %s mandatory because of %s", other->action->uuid, action->uuid); other_changed = TRUE; } else { do_crm_log(log_level, " Ignoring implies left"); } } if(local_type & pe_order_implies_right) { if(action->optional == FALSE) { /* nothing to do */ do_crm_log(log_level+1, " Ignoring implies right - redundant"); } else if(other->action->optional == FALSE) { action->optional = FALSE; do_crm_log(log_level-1, " * (implies right) Marking action %s mandatory because of %s", action->uuid, other->action->uuid); changed = TRUE; } else { do_crm_log(log_level, " Ignoring implies right"); } } if(other_changed) { do_crm_log(log_level, "%s changed, processing after list", other->action->uuid); update_action(other->action); slist_iter( before_other, action_wrapper_t, other->action->actions_after, lpc2, do_crm_log(log_level, "%s changed, processing %s", other->action->uuid, before_other->action->uuid); update_action(before_other->action); ); } ); if(changed) { do_crm_log(log_level, "%s changed, processing after list", action->uuid); slist_iter( other, action_wrapper_t, action->actions_after, lpc, do_crm_log(log_level, "%s changed, processing %s", action->uuid, other->action->uuid); update_action(other->action); ); } return FALSE; } gboolean shutdown_constraints( node_t *node, action_t *shutdown_op, pe_working_set_t *data_set) { /* add the stop to the before lists so it counts as a pre-req * for the shutdown */ slist_iter( rsc, resource_t, node->details->running_rsc, lpc, if(rsc->is_managed == FALSE) { continue; } custom_action_order( rsc, stop_key(rsc), NULL, NULL, crm_strdup(CRM_OP_SHUTDOWN), shutdown_op, pe_order_implies_left, data_set); ); return TRUE; } gboolean stonith_constraints( node_t *node, action_t *stonith_op, pe_working_set_t *data_set) { CRM_CHECK(stonith_op != NULL, return FALSE); /* * Make sure the stonith OP occurs before we start any shared resources */ if(stonith_op != NULL) { slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->stonith_ordering(rsc, stonith_op, data_set); ); } /* add the stonith OP as a stop pre-req and the mark the stop * as a pseudo op - since its now redundant */ return TRUE; } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(value)); } crm_data_t * action2xml(action_t *action, gboolean as_input) { gboolean needs_node_info = TRUE; crm_data_t * action_xml = NULL; crm_data_t * args_xml = NULL; char *action_id_s = NULL; if(action == NULL) { return NULL; } crm_debug_4("Dumping action %d as XML", action->id); if(safe_str_eq(action->task, CRM_OP_FENCE)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); /* needs_node_info = FALSE; */ } else if(safe_str_eq(action->task, CRM_OP_SHUTDOWN)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); } else if(safe_str_eq(action->task, CRM_OP_LRM_REFRESH)) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); /* } else if(safe_str_eq(action->task, CRMD_ACTION_PROBED)) { */ /* action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); */ } else if(action->pseudo) { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_PSEUDO_EVENT); needs_node_info = FALSE; } else { action_xml = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP); } action_id_s = crm_itoa(action->id); crm_xml_add(action_xml, XML_ATTR_ID, action_id_s); crm_free(action_id_s); crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task); if(action->rsc != NULL && action->rsc->clone_name != NULL) { char *clone_key = NULL; const char *interval_s = g_hash_table_lookup(action->meta, "interval"); int interval = crm_parse_int(interval_s, "0"); if(safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) { const char *n_type = g_hash_table_lookup( action->extra, crm_meta_name("notify_type")); const char *n_task = g_hash_table_lookup( action->extra, crm_meta_name("notify_operation")); CRM_CHECK(n_type != NULL, ;); CRM_CHECK(n_task != NULL, ;); clone_key = generate_notify_key(action->rsc->clone_name, n_type, n_task); } else { clone_key = generate_op_key(action->rsc->clone_name, action->task, interval); } crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key); crm_xml_add(action_xml, "internal_"XML_LRM_ATTR_TASK_KEY, action->uuid); crm_free(clone_key); } else { crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid); } if(needs_node_info && action->node != NULL) { crm_xml_add(action_xml, XML_LRM_ATTR_TARGET, action->node->details->uname); crm_xml_add(action_xml, XML_LRM_ATTR_TARGET_UUID, action->node->details->id); } if(action->failure_is_fatal == FALSE) { add_hash_param(action->meta, XML_ATTR_TE_ALLOWFAIL, XML_BOOLEAN_TRUE); } if(as_input) { return action_xml; } if(action->notify_keys != NULL) { g_hash_table_foreach( action->notify_keys, dup_attr, action->meta); } if(action->rsc != NULL && action->pseudo == FALSE) { int lpc = 0; crm_data_t *rsc_xml = create_xml_node( action_xml, crm_element_name(action->rsc->xml)); const char *attr_list[] = { XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER, XML_ATTR_TYPE }; if(action->rsc->clone_name != NULL) { crm_debug("Using clone name %s for %s", action->rsc->clone_name, action->rsc->id); crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name); crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id); } else { crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id); crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->long_name); } for(lpc = 0; lpc < DIMOF(attr_list); lpc++) { crm_xml_add(rsc_xml, attr_list[lpc], g_hash_table_lookup(action->rsc->meta, attr_list[lpc])); } } args_xml = create_xml_node(action_xml, XML_TAG_ATTRS); crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET); g_hash_table_foreach(action->extra, hash2field, args_xml); if(action->rsc != NULL && safe_str_neq(action->task, CRMD_ACTION_STOP)) { g_hash_table_foreach(action->rsc->parameters, hash2field, args_xml); } g_hash_table_foreach(action->meta, hash2metafield, args_xml); if(action->rsc != NULL) { int lpc = 0; const char *key = NULL; const char *value = NULL; const char *meta_list[] = { XML_RSC_ATTR_UNIQUE, XML_RSC_ATTR_INCARNATION, XML_RSC_ATTR_INCARNATION_MAX, XML_RSC_ATTR_INCARNATION_NODEMAX, XML_RSC_ATTR_MASTER_MAX, XML_RSC_ATTR_MASTER_NODEMAX, }; for(lpc = 0; lpc < DIMOF(meta_list); lpc++) { key = meta_list[lpc]; value = g_hash_table_lookup(action->rsc->meta, key); if(value != NULL) { char *crm_name = crm_concat(CRM_META, key, '_'); crm_xml_add(args_xml, crm_name, value); crm_free(crm_name); } } } crm_log_xml_debug_4(action_xml, "dumped action"); return action_xml; } static gboolean should_dump_action(action_t *action) { const char * interval = NULL; CRM_CHECK(action != NULL, return FALSE); interval = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); if(action->optional) { crm_debug_5("action %d (%s) was optional", action->id, action->uuid); return FALSE; } else if(action->runnable == FALSE) { crm_debug_5("action %d (%s) was not runnable", action->id, action->uuid); return FALSE; } else if(action->dumped) { crm_debug_5("action %d (%s) was already dumped", action->id, action->uuid); return FALSE; } else if(action->rsc != NULL && action->rsc->is_managed == FALSE) { /* make sure probes go through */ if(safe_str_neq(action->task, CRMD_ACTION_STATUS)) { pe_warn("action %d (%s) was for an unmanaged resource (%s)", action->id, action->uuid, action->rsc->id); return FALSE; } if(interval != NULL && safe_str_neq(interval, "0")) { pe_warn("action %d (%s) was for an unmanaged resource (%s)", action->id, action->uuid, action->rsc->id); return FALSE; } } if(action->pseudo || safe_str_eq(action->task, CRM_OP_FENCE) || safe_str_eq(action->task, CRM_OP_SHUTDOWN)) { /* skip the next checks */ return TRUE; } if(action->node == NULL) { pe_err("action %d (%s) was not allocated", action->id, action->uuid); log_action(LOG_DEBUG, "Unallocated action", action, FALSE); return FALSE; } else if(action->node->details->online == FALSE) { pe_err("action %d was (%s) scheduled for offline node", action->id, action->uuid); log_action(LOG_DEBUG, "Action for offline node", action, FALSE); return FALSE; #if 0 /* but this would also affect resources that can be safely * migrated before a fencing op */ } else if(action->node->details->unclean == FALSE) { pe_err("action %d was (%s) scheduled for unclean node", action->id, action->uuid); log_action(LOG_DEBUG, "Action for unclean node", action, FALSE); return FALSE; #endif } return TRUE; } /* lowest to highest */ static gint sort_action_id(gconstpointer a, gconstpointer b) { const action_wrapper_t *action_wrapper2 = (const action_wrapper_t*)a; const action_wrapper_t *action_wrapper1 = (const action_wrapper_t*)b; if(a == NULL) { return 1; } if(b == NULL) { return -1; } if(action_wrapper1->action->id > action_wrapper2->action->id) { return -1; } if(action_wrapper1->action->id < action_wrapper2->action->id) { return 1; } return 0; } static gboolean should_dump_input(int last_action, action_t *action, action_wrapper_t *wrapper) { wrapper->state = pe_link_not_dumped; if(last_action == wrapper->action->id) { crm_debug_2("Input (%d) %s duplicated", wrapper->action->id, wrapper->action->uuid); wrapper->state = pe_link_dup; return FALSE; } else if(wrapper->type == pe_order_none) { crm_debug_2("Input (%d) %s suppressed", wrapper->action->id, wrapper->action->uuid); return FALSE; } else if(wrapper->action->optional == TRUE) { crm_debug_2("Input (%d) %s optional", wrapper->action->id, wrapper->action->uuid); return FALSE; } else if(wrapper->action->runnable == FALSE && wrapper->action->pseudo == FALSE && wrapper->type == pe_order_optional) { crm_debug("Input (%d) %s optional (ordering)", wrapper->action->id, wrapper->action->uuid); return FALSE; } else if(action->pseudo && (wrapper->type & pe_order_stonith_stop)) { crm_debug("Input (%d) %s suppressed", wrapper->action->id, wrapper->action->uuid); return FALSE; } + crm_debug_3("Input (%d) %s n=%p p=%d r=%d f=0x%.6x dumped for %s", + wrapper->action->id, + wrapper->action->uuid, + wrapper->action->node, + wrapper->action->pseudo, + wrapper->action->runnable, + wrapper->type, + action->uuid); return TRUE; } void graph_element_from_action(action_t *action, pe_working_set_t *data_set) { int last_action = -1; int synapse_priority = 0; crm_data_t * syn = NULL; crm_data_t * set = NULL; crm_data_t * in = NULL; crm_data_t * input = NULL; crm_data_t * xml_action = NULL; if(should_dump_action(action) == FALSE) { return; } action->dumped = TRUE; syn = create_xml_node(data_set->graph, "synapse"); set = create_xml_node(syn, "action_set"); in = create_xml_node(syn, "inputs"); crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse); data_set->num_synapse++; if(action->rsc != NULL) { synapse_priority = action->rsc->priority; } if(action->priority > synapse_priority) { synapse_priority = action->priority; } if(synapse_priority > 0) { crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority); } xml_action = action2xml(action, FALSE); add_node_nocopy(set, crm_element_name(xml_action), xml_action); action->actions_before = g_list_sort( action->actions_before, sort_action_id); slist_iter(wrapper,action_wrapper_t,action->actions_before,lpc, if(should_dump_input(last_action, action, wrapper) == FALSE) { continue; } wrapper->state = pe_link_dumped; CRM_CHECK(last_action < wrapper->action->id, ;); last_action = wrapper->action->id; input = create_xml_node(in, "trigger"); xml_action = action2xml(wrapper->action, TRUE); add_node_nocopy(input, crm_element_name(xml_action), xml_action); ); }