diff --git a/pengine/allocate.c b/pengine/allocate.c index 8540ce44a3..a2885e3e66 100644 --- a/pengine/allocate.c +++ b/pengine/allocate.c @@ -1,2281 +1,2282 @@ /* * 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 CRM_TRACE_INIT_DATA(pe_allocate); void set_alloc_actions(pe_working_set_t * data_set); void migrate_reload_madness(pe_working_set_t * data_set); resource_alloc_functions_t resource_class_alloc_functions[] = { { native_merge_weights, native_color, native_create_actions, native_create_probe, native_internal_constraints, native_rsc_colocation_lh, native_rsc_colocation_rh, native_rsc_location, native_action_flags, native_update_actions, native_expand, native_append_meta, }, { group_merge_weights, group_color, group_create_actions, native_create_probe, group_internal_constraints, group_rsc_colocation_lh, group_rsc_colocation_rh, group_rsc_location, group_action_flags, group_update_actions, group_expand, group_append_meta, }, { clone_merge_weights, clone_color, clone_create_actions, clone_create_probe, clone_internal_constraints, clone_rsc_colocation_lh, clone_rsc_colocation_rh, clone_rsc_location, clone_action_flags, clone_update_actions, clone_expand, clone_append_meta, }, { master_merge_weights, master_color, master_create_actions, clone_create_probe, master_internal_constraints, clone_rsc_colocation_lh, master_rsc_colocation_rh, clone_rsc_location, clone_action_flags, clone_update_actions, clone_expand, master_append_meta, } }; static gboolean check_rsc_parameters(resource_t * rsc, node_t * node, xmlNode * rsc_entry, pe_working_set_t * data_set) { int attr_lpc = 0; gboolean force_restart = FALSE; gboolean delete_resource = FALSE; const char *value = NULL; const char *old_value = NULL; const char *attr_list[] = { XML_ATTR_TYPE, XML_AGENT_ATTR_CLASS, XML_AGENT_ATTR_PROVIDER }; for (; attr_lpc < DIMOF(attr_list); attr_lpc++) { value = crm_element_value(rsc->xml, attr_list[attr_lpc]); old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]); if (value == old_value /* ie. NULL */ || crm_str_eq(value, old_value, TRUE)) { continue; } force_restart = TRUE; crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s", rsc->id, node->details->uname, attr_list[attr_lpc], crm_str(old_value), crm_str(value)); } if (force_restart) { /* make sure the restart happens */ stop_action(rsc, node, FALSE); set_bit(rsc->flags, pe_rsc_start_pending); delete_resource = TRUE; } return delete_resource; } static void CancelXmlOp(resource_t * rsc, xmlNode * xml_op, node_t * active_node, const char *reason, pe_working_set_t * data_set) { int interval = 0; action_t *cancel = NULL; char *key = NULL; const char *task = NULL; const char *call_id = NULL; const char *interval_s = NULL; CRM_CHECK(xml_op != NULL, return); CRM_CHECK(active_node != NULL, return); task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID); interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); /* we need to reconstruct the key because of the way we used to construct resource IDs */ key = generate_op_key(rsc->id, task, interval); crm_info("Action %s on %s will be stopped: %s", key, active_node->details->uname, reason ? reason : "unknown"); cancel = custom_action(rsc, strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set); free(cancel->task); cancel->task = strdup(RSC_CANCEL); add_hash_param(cancel->meta, XML_LRM_ATTR_TASK, task); add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id); add_hash_param(cancel->meta, XML_LRM_ATTR_INTERVAL, interval_s); custom_action_order(rsc, stop_key(rsc), NULL, rsc, NULL, cancel, pe_order_optional, data_set); free(key); key = NULL; } static gboolean check_action_definition(resource_t * rsc, node_t * active_node, xmlNode * xml_op, pe_working_set_t * data_set) { char *key = NULL; int interval = 0; const char *interval_s = NULL; const op_digest_cache_t *digest_data = NULL; gboolean did_change = FALSE; const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *op_version; CRM_CHECK(active_node != NULL, return FALSE); if (safe_str_eq(task, RSC_STOP)) { return FALSE; } interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); if (interval > 0) { xmlNode *op_match = NULL; /* we need to reconstruct the key because of the way we used to construct resource IDs */ key = generate_op_key(rsc->id, task, interval); pe_rsc_trace(rsc, "Checking parameters for %s", key); op_match = find_rsc_op_entry(rsc, key); if (op_match == NULL && is_set(data_set->flags, pe_flag_stop_action_orphans)) { CancelXmlOp(rsc, xml_op, active_node, "orphan", data_set); free(key); return TRUE; } else if (op_match == NULL) { pe_rsc_debug(rsc, "Orphan action detected: %s on %s", key, active_node->details->uname); free(key); return TRUE; } free(key); key = NULL; } if (interval == 0 && safe_str_eq(task, RSC_STATUS)) { /* Reload based on the start action not a probe */ task = RSC_START; } else if (interval == 0 && safe_str_eq(task, RSC_MIGRATED)) { /* Reload based on the start action not a migrate */ task = RSC_START; } digest_data = rsc_action_digest_cmp(rsc, xml_op, active_node, data_set); op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); /* Changes that force a restart */ if (digest_data->rc == RSC_DIGEST_RESTART) { const char *digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); did_change = TRUE; key = generate_op_key(rsc->id, task, interval); crm_log_xml_info(digest_data->params_restart, "params:restart"); crm_info("Parameters to %s on %s changed: was %s vs. now %s (restart:%s) %s", key, active_node->details->uname, crm_str(digest_restart), digest_data->digest_restart_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); } else if ((digest_data->rc == RSC_DIGEST_ALL) || (digest_data->rc == RSC_DIGEST_UNKNOWN)) { /* Changes that can potentially be handled by a reload */ const char *digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); const char *digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); did_change = TRUE; crm_log_xml_info(digest_data->params_all, "params:reload"); key = generate_op_key(rsc->id, task, interval); crm_info("Parameters to %s on %s changed: was %s vs. now %s (reload:%s) %s", key, active_node->details->uname, crm_str(digest_all), digest_data->digest_all_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); if (interval > 0) { action_t *op = NULL; #if 0 /* Always reload/restart the entire resource */ op = custom_action(rsc, start_key(rsc), RSC_START, NULL, FALSE, TRUE, data_set); update_action_flags(op, pe_action_allow_reload_conversion); #else /* Re-sending the recurring op is sufficient - the old one will be cancelled automatically */ op = custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); custom_action_order(rsc, start_key(rsc), NULL, NULL, NULL, op, pe_order_runnable_left, data_set); #endif } else if (digest_restart) { pe_rsc_trace(rsc, "Reloading '%s' action for resource %s", task, rsc->id); /* Allow this resource to reload - unless something else causes a full restart */ set_bit(rsc->flags, pe_rsc_try_reload); /* Create these for now, it keeps the action IDs the same in the regression outputs */ custom_action(rsc, key, task, NULL, TRUE, TRUE, data_set); } else { pe_rsc_trace(rsc, "Resource %s doesn't know how to reload", rsc->id); /* Re-send the start/demote/promote op * Recurring ops will be detected independantly */ custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); } } return did_change; } extern gboolean DeleteRsc(resource_t * rsc, node_t * node, gboolean optional, pe_working_set_t * data_set); static void check_actions_for(xmlNode * rsc_entry, resource_t * rsc, node_t * node, pe_working_set_t * data_set) { GListPtr gIter = NULL; int offset = -1; int interval = 0; int stop_index = 0; int start_index = 0; const char *task = NULL; const char *interval_s = NULL; xmlNode *rsc_op = NULL; GListPtr op_list = NULL; GListPtr sorted_op_list = NULL; gboolean is_probe = FALSE; gboolean did_change = FALSE; CRM_CHECK(node != NULL, return); if (is_set(rsc->flags, pe_rsc_orphan)) { - pe_rsc_trace(rsc, "Skipping param check for %s: orphan", rsc->id); + pe_rsc_trace(rsc, "Skipping param check for %s and deleting: orphan", rsc->id); + DeleteRsc(rsc, node, FALSE, data_set); return; } else if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) { pe_rsc_trace(rsc, "Skipping param check for %s: no longer active on %s", rsc->id, node->details->uname); return; } pe_rsc_trace(rsc, "Processing %s on %s", rsc->id, node->details->uname); if (check_rsc_parameters(rsc, node, rsc_entry, data_set)) { DeleteRsc(rsc, node, FALSE, data_set); } for (rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) { if (crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) { op_list = g_list_prepend(op_list, rsc_op); } } sorted_op_list = g_list_sort(op_list, sort_op_by_callid); calculate_active_ops(sorted_op_list, &start_index, &stop_index); for (gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) { xmlNode *rsc_op = (xmlNode *) gIter->data; offset++; if (start_index < stop_index) { /* stopped */ continue; } else if (offset < start_index) { /* action occurred prior to a start */ continue; } is_probe = FALSE; did_change = FALSE; task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK); interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL); interval = crm_parse_int(interval_s, "0"); if (interval == 0 && safe_str_eq(task, RSC_STATUS)) { is_probe = TRUE; } if (interval > 0 && is_set(data_set->flags, pe_flag_maintenance_mode)) { CancelXmlOp(rsc, rsc_op, node, "maintenance mode", data_set); } else if (is_probe || safe_str_eq(task, RSC_START) || interval > 0 || safe_str_eq(task, RSC_MIGRATED)) { did_change = check_action_definition(rsc, node, rsc_op, data_set); } if (did_change && get_failcount(node, rsc, NULL, data_set)) { char *key = NULL; action_t *action_clear = NULL; key = generate_op_key(rsc->id, CRM_OP_CLEAR_FAILCOUNT, 0); action_clear = custom_action(rsc, key, CRM_OP_CLEAR_FAILCOUNT, node, FALSE, TRUE, data_set); set_bit(action_clear->flags, pe_action_runnable); } } g_list_free(sorted_op_list); } static GListPtr find_rsc_list(GListPtr result, resource_t * rsc, const char *id, gboolean renamed_clones, gboolean partial, pe_working_set_t * data_set) { GListPtr gIter = NULL; gboolean match = FALSE; if (id == NULL) { return NULL; } else if (rsc == NULL && data_set) { for (gIter = data_set->resources; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; result = find_rsc_list(result, child, id, renamed_clones, partial, NULL); } return result; } else if (rsc == NULL) { return NULL; } if (partial) { if (strstr(rsc->id, id)) { match = TRUE; } else if (renamed_clones && rsc->clone_name && strstr(rsc->clone_name, id)) { match = TRUE; } } else { if (strcmp(rsc->id, id) == 0) { match = TRUE; } else if (renamed_clones && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) { match = TRUE; } } if (match) { result = g_list_prepend(result, rsc); } if (rsc->children) { gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; result = find_rsc_list(result, child, id, renamed_clones, partial, NULL); } } return result; } static void check_actions(pe_working_set_t * data_set) { const char *id = NULL; node_t *node = NULL; xmlNode *lrm_rscs = NULL; xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, data_set->input); xmlNode *node_state = NULL; for (node_state = __xml_first_child(status); node_state != NULL; node_state = __xml_next(node_state)) { if (crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) { id = crm_element_value(node_state, XML_ATTR_ID); lrm_rscs = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE); lrm_rscs = find_xml_node(lrm_rscs, XML_LRM_TAG_RESOURCES, FALSE); node = pe_find_node_id(data_set->nodes, id); if (node == NULL) { continue; } else if (can_run_resources(node) == FALSE) { crm_trace("Skipping param check for %s: cant run resources", node->details->uname); continue; } crm_trace("Processing node %s", node->details->uname); if (node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { xmlNode *rsc_entry = NULL; for (rsc_entry = __xml_first_child(lrm_rscs); rsc_entry != NULL; rsc_entry = __xml_next(rsc_entry)) { if (crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) { if (xml_has_children(rsc_entry)) { GListPtr gIter = NULL; GListPtr result = NULL; const char *rsc_id = ID(rsc_entry); CRM_CHECK(rsc_id != NULL, return); result = find_rsc_list(NULL, NULL, rsc_id, TRUE, FALSE, data_set); for (gIter = result; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; check_actions_for(rsc_entry, rsc, node, data_set); } g_list_free(result); } } } } } } } static gboolean apply_placement_constraints(pe_working_set_t * data_set) { GListPtr gIter = NULL; crm_trace("Applying constraints..."); for (gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) { rsc_to_node_t *cons = (rsc_to_node_t *) gIter->data; cons->rsc_lh->cmds->rsc_location(cons->rsc_lh, cons); } return TRUE; } static gboolean failcount_clear_action_exists(node_t *node, resource_t *rsc) { char *key = crm_concat(rsc->id, CRM_OP_CLEAR_FAILCOUNT, '_'); gboolean rc = FALSE; if (find_actions_exact(rsc->actions, key, node)) { rc = TRUE; } free(key); return rc; } static void common_apply_stickiness(resource_t * rsc, node_t * node, pe_working_set_t * data_set) { int fail_count = 0; resource_t *failed = rsc; if (rsc->children) { GListPtr gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; common_apply_stickiness(child_rsc, node, data_set); } return; } if (is_set(rsc->flags, pe_rsc_managed) && rsc->stickiness != 0 && g_list_length(rsc->running_on) == 1) { node_t *current = pe_find_node_id(rsc->running_on, node->details->id); node_t *match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); if (current == NULL) { } else if (match != NULL || is_set(data_set->flags, pe_flag_symmetric_cluster)) { resource_t *sticky_rsc = rsc; resource_location(sticky_rsc, node, rsc->stickiness, "stickiness", data_set); pe_rsc_debug(sticky_rsc, "Resource %s: preferring current location" " (node=%s, weight=%d)", sticky_rsc->id, node->details->uname, rsc->stickiness); } else { GHashTableIter iter; node_t *nIter = NULL; pe_rsc_debug(rsc, "Ignoring stickiness for %s: the cluster is asymmetric" " and node %s is not explicitly allowed", rsc->id, node->details->uname); g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&nIter)) { crm_err("%s[%s] = %d", rsc->id, nIter->details->uname, nIter->weight); } } } /* only check failcount here if a failcount clear action * has not already been placed for this resource on the node. * There is no sense in potentially forcing the rsc from this * node if the failcount is being reset anyway. */ if (failcount_clear_action_exists(node, rsc) == FALSE) { fail_count = get_failcount_all(node, rsc, NULL, data_set); } if (fail_count > 0 && rsc->migration_threshold != 0) { if (is_not_set(rsc->flags, pe_rsc_unique)) { failed = uber_parent(rsc); } if (rsc->migration_threshold <= fail_count) { resource_location(failed, node, -INFINITY, "__fail_limit__", data_set); crm_warn("Forcing %s away from %s after %d failures (max=%d)", failed->id, node->details->uname, fail_count, rsc->migration_threshold); } else { crm_info("%s can fail %d more times on %s before being forced off", failed->id, rsc->migration_threshold - fail_count, node->details->uname); } } } static void complex_set_cmds(resource_t * rsc) { GListPtr gIter = rsc->children; rsc->cmds = &resource_class_alloc_functions[rsc->variant]; for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; complex_set_cmds(child_rsc); } } void set_alloc_actions(pe_working_set_t * data_set) { GListPtr gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; complex_set_cmds(rsc); } } static void calculate_system_health(gpointer gKey, gpointer gValue, gpointer user_data) { const char *key = (const char *)gKey; const char *value = (const char *)gValue; int *system_health = (int *)user_data; if (!gKey || !gValue || !user_data) { return; } /* Does it start with #health? */ if (0 == strncmp(key, "#health", 7)) { int score; /* Convert the value into an integer */ score = char2score(value); /* Add it to the running total */ *system_health = merge_weights(score, *system_health); } } static gboolean apply_system_health(pe_working_set_t * data_set) { GListPtr gIter = NULL; const char *health_strategy = pe_pref(data_set->config_hash, "node-health-strategy"); if (health_strategy == NULL || safe_str_eq(health_strategy, "none")) { /* Prevent any accidental health -> score translation */ node_score_red = 0; node_score_yellow = 0; node_score_green = 0; return TRUE; } else if (safe_str_eq(health_strategy, "migrate-on-red")) { /* Resources on nodes which have health values of red are * weighted away from that node. */ node_score_red = -INFINITY; node_score_yellow = 0; node_score_green = 0; } else if (safe_str_eq(health_strategy, "only-green")) { /* Resources on nodes which have health values of red or yellow * are forced away from that node. */ node_score_red = -INFINITY; node_score_yellow = -INFINITY; node_score_green = 0; } else if (safe_str_eq(health_strategy, "progressive")) { /* Same as the above, but use the r/y/g scores provided by the user * Defaults are provided by the pe_prefs table */ } else if (safe_str_eq(health_strategy, "custom")) { /* Requires the admin to configure the rsc_location constaints for * processing the stored health scores */ /* TODO: Check for the existance of appropriate node health constraints */ return TRUE; } else { crm_err("Unknown node health strategy: %s", health_strategy); return FALSE; } crm_info("Applying automated node health strategy: %s", health_strategy); for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { int system_health = 0; node_t *node = (node_t *) gIter->data; /* Search through the node hash table for system health entries. */ g_hash_table_foreach(node->details->attrs, calculate_system_health, &system_health); crm_info(" Node %s has an combined system health of %d", node->details->uname, system_health); /* If the health is non-zero, then create a new rsc2node so that the * weight will be added later on. */ if (system_health != 0) { GListPtr gIter2 = data_set->resources; for (; gIter2 != NULL; gIter2 = gIter2->next) { resource_t *rsc = (resource_t *) gIter2->data; rsc2node_new(health_strategy, rsc, system_health, node, data_set); } } } return TRUE; } gboolean stage0(pe_working_set_t * data_set) { xmlNode *cib_constraints = get_object_root(XML_CIB_TAG_CONSTRAINTS, data_set->input); if (data_set->input == NULL) { return FALSE; } if (is_set(data_set->flags, pe_flag_have_status) == FALSE) { crm_trace("Calculating status"); cluster_status(data_set); } set_alloc_actions(data_set); apply_system_health(data_set); unpack_constraints(cib_constraints, data_set); return TRUE; } static void wait_for_probe(resource_t * rsc, const char *action, action_t * probe_complete, pe_working_set_t * data_set) { if (probe_complete == NULL) { return; } if (rsc->children) { GListPtr gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; wait_for_probe(child, action, probe_complete, data_set); } } else { char *key = NULL; if(safe_str_eq(action, RSC_STOP) && g_list_length(rsc->running_on) == 1) { node_t *node = (node_t *) rsc->running_on->data; /* Stop actions on nodes that are shutting down do not need to wait for probes to complete * Doing so prevents node shutdown in the presence of nodes that are coming up * The purpose of waiting is to not stop resources until we know for sure the * intended destination is able to take them */ if(node && node->details->shutdown) { crm_debug("Skipping %s before %s_%s_0 due to %s shutdown", probe_complete->uuid, rsc->id, action, node->details->uname); return; } } key = generate_op_key(rsc->id, action, 0); custom_action_order(NULL, NULL, probe_complete, rsc, key, NULL, pe_order_optional, data_set); } } /* * Check nodes for resources started outside of the LRM */ gboolean probe_resources(pe_working_set_t * data_set) { action_t *probe_complete = NULL; action_t *probe_node_complete = NULL; GListPtr gIter = NULL; GListPtr gIter2 = NULL; gIter = data_set->nodes; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; const char *probed = g_hash_table_lookup(node->details->attrs, CRM_OP_PROBED); if (node->details->online == FALSE) { continue; } else if (node->details->unclean) { continue; } else if (probe_complete == NULL) { probe_complete = get_pseudo_op(CRM_OP_PROBED, data_set); } if (probed != NULL && crm_is_true(probed) == FALSE) { action_t *probe_op = custom_action(NULL, strdup(CRM_OP_REPROBE), CRM_OP_REPROBE, node, FALSE, TRUE, data_set); add_hash_param(probe_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); continue; } probe_node_complete = custom_action(NULL, strdup(CRM_OP_PROBED), CRM_OP_PROBED, node, FALSE, TRUE, data_set); if (crm_is_true(probed)) { crm_trace("unset"); update_action_flags(probe_node_complete, pe_action_optional); } else { crm_trace("set"); update_action_flags(probe_node_complete, pe_action_optional | pe_action_clear); } crm_trace("%s - %d", node->details->uname, probe_node_complete->flags & pe_action_optional); probe_node_complete->priority = INFINITY; add_hash_param(probe_node_complete->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); if (node->details->pending) { update_action_flags(probe_node_complete, pe_action_runnable | pe_action_clear); crm_info("Action %s on %s is unrunnable (pending)", probe_node_complete->uuid, probe_node_complete->node->details->uname); } order_actions(probe_node_complete, probe_complete, pe_order_runnable_left /*|pe_order_implies_then */ ); gIter2 = data_set->resources; for (; gIter2 != NULL; gIter2 = gIter2->next) { resource_t *rsc = (resource_t *) gIter2->data; if (rsc->cmds->create_probe(rsc, node, probe_node_complete, FALSE, data_set)) { update_action_flags(probe_complete, pe_action_optional | pe_action_clear); update_action_flags(probe_node_complete, pe_action_optional | pe_action_clear); wait_for_probe(rsc, RSC_START, probe_complete, data_set); } } } gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; wait_for_probe(rsc, RSC_STOP, probe_complete, data_set); } return TRUE; } /* * Count how many valid nodes we have (so we know the maximum number of * colors we can resolve). * * Apply node constraints (ie. filter the "allowed_nodes" part of resources */ gboolean stage2(pe_working_set_t * data_set) { GListPtr gIter = NULL; crm_trace("Applying placement constraints"); gIter = data_set->nodes; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; if (node == NULL) { /* error */ } else if (node->weight >= 0.0 /* global weight */ && node->details->online && node->details->type == node_member) { data_set->max_valid_nodes++; } } apply_placement_constraints(data_set); gIter = data_set->nodes; for (; gIter != NULL; gIter = gIter->next) { GListPtr gIter2 = NULL; node_t *node = (node_t *) gIter->data; gIter2 = data_set->resources; for (; gIter2 != NULL; gIter2 = gIter2->next) { resource_t *rsc = (resource_t *) gIter2->data; common_apply_stickiness(rsc, node, data_set); } } return TRUE; } /* * Create internal resource constraints before allocation */ gboolean stage3(pe_working_set_t * data_set) { GListPtr gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; rsc->cmds->internal_constraints(rsc, data_set); } return TRUE; } /* * Check for orphaned or redefined actions */ gboolean stage4(pe_working_set_t * data_set) { check_actions(data_set); return TRUE; } static gint sort_rsc_process_order(gconstpointer a, gconstpointer b, gpointer data) { int rc = 0; int r1_weight = -INFINITY; int r2_weight = -INFINITY; const char *reason = "existance"; const GListPtr nodes = (GListPtr) data; resource_t *resource1 = (resource_t *) convert_const_pointer(a); resource_t *resource2 = (resource_t *) convert_const_pointer(b); node_t *node = NULL; GListPtr gIter = NULL; GHashTable *r1_nodes = NULL; GHashTable *r2_nodes = NULL; if (a == NULL && b == NULL) { goto done; } if (a == NULL) { return 1; } if (b == NULL) { return -1; } reason = "priority"; r1_weight = resource1->priority; r2_weight = resource2->priority; if (r1_weight > r2_weight) { rc = -1; goto done; } if (r1_weight < r2_weight) { rc = 1; goto done; } reason = "no node list"; if (nodes == NULL) { goto done; } r1_nodes = rsc_merge_weights(resource1, resource1->id, NULL, NULL, 1, pe_weights_forward | pe_weights_init); dump_node_scores(LOG_TRACE, NULL, resource1->id, r1_nodes); r2_nodes = rsc_merge_weights(resource2, resource2->id, NULL, NULL, 1, pe_weights_forward | pe_weights_init); dump_node_scores(LOG_TRACE, NULL, resource2->id, r2_nodes); /* Current location score */ reason = "current location"; r1_weight = -INFINITY; r2_weight = -INFINITY; if (resource1->running_on) { node = g_list_nth_data(resource1->running_on, 0); node = g_hash_table_lookup(r1_nodes, node->details->id); r1_weight = node->weight; } if (resource2->running_on) { node = g_list_nth_data(resource2->running_on, 0); node = g_hash_table_lookup(r2_nodes, node->details->id); r2_weight = node->weight; } if (r1_weight > r2_weight) { rc = -1; goto done; } if (r1_weight < r2_weight) { rc = 1; goto done; } reason = "score"; for (gIter = nodes; gIter != NULL; gIter = gIter->next) { node_t *r1_node = NULL; node_t *r2_node = NULL; node = (node_t *) gIter->data; r1_weight = -INFINITY; if (r1_nodes) { r1_node = g_hash_table_lookup(r1_nodes, node->details->id); } if (r1_node) { r1_weight = r1_node->weight; } r2_weight = -INFINITY; if (r2_nodes) { r2_node = g_hash_table_lookup(r2_nodes, node->details->id); } if (r2_node) { r2_weight = r2_node->weight; } if (r1_weight > r2_weight) { rc = -1; goto done; } if (r1_weight < r2_weight) { rc = 1; goto done; } } done: if (r1_nodes) { g_hash_table_destroy(r1_nodes); } if (r2_nodes) { g_hash_table_destroy(r2_nodes); } crm_trace( "%s (%d) %c %s (%d) on %s: %s", resource1->id, r1_weight, rc < 0 ? '>' : rc > 0 ? '<' : '=', resource2->id, r2_weight, node ? node->details->id : "n/a", reason); return rc; } gboolean stage5(pe_working_set_t * data_set) { GListPtr gIter = NULL; if (safe_str_neq(data_set->placement_strategy, "default")) { GListPtr nodes = g_list_copy(data_set->nodes); nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL); data_set->resources = g_list_sort_with_data(data_set->resources, sort_rsc_process_order, nodes); g_list_free(nodes); } gIter = data_set->nodes; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; dump_node_capacity(show_utilization ? 0 : utilization_log_level, "Original", node); } crm_trace("Allocating services"); /* Take (next) highest resource, assign it and create its actions */ gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; pe_rsc_trace(rsc, "Allocating: %s", rsc->id); rsc->cmds->allocate(rsc, NULL, data_set); } gIter = data_set->nodes; for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; dump_node_capacity(show_utilization ? 0 : utilization_log_level, "Remaining", node); } if (is_set(data_set->flags, pe_flag_startup_probes)) { crm_trace("Calculating needed probes"); /* This code probably needs optimization * ptest -x with 100 nodes, 100 clones and clone-max=100: With probes: ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:292 Check actions ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: do_calculations: pengine.c:299 Allocate resources ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: stage5: allocate.c:881 Allocating services ptest[14781]: 2010/09/27_17:56:49 notice: TRACE: stage5: allocate.c:894 Calculating needed probes ptest[14781]: 2010/09/27_17:56:51 notice: TRACE: stage5: allocate.c:899 Creating actions ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: stage5: allocate.c:905 Creating done ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints 36s ptest[14781]: 2010/09/27_17:57:28 notice: TRACE: do_calculations: pengine.c:320 Create transition graph Without probes: ptest[14637]: 2010/09/27_17:56:21 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:292 Check actions ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: do_calculations: pengine.c:299 Allocate resources ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: stage5: allocate.c:881 Allocating services ptest[14637]: 2010/09/27_17:56:24 notice: TRACE: stage5: allocate.c:899 Creating actions ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: stage5: allocate.c:905 Creating done ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:320 Create transition graph */ probe_resources(data_set); } crm_trace("Creating actions"); gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; rsc->cmds->create_actions(rsc, data_set); } crm_trace("Creating done"); return TRUE; } static gboolean is_managed(const resource_t * rsc) { GListPtr gIter = rsc->children; if (is_set(rsc->flags, pe_rsc_managed)) { return TRUE; } for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; if (is_managed(child_rsc)) { return TRUE; } } return FALSE; } static gboolean any_managed_resouces(pe_working_set_t * data_set) { GListPtr gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; if (is_managed(rsc)) { return TRUE; } } return FALSE; } action_t * pe_fence_op(node_t *node, const char *op, pe_working_set_t * data_set) { action_t *stonith_op = custom_action( NULL, g_strdup_printf("%s-%s", CRM_OP_FENCE, node->details->uname), CRM_OP_FENCE, node, FALSE, TRUE, data_set); add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET, node->details->uname); add_hash_param(stonith_op->meta, XML_LRM_ATTR_TARGET_UUID, node->details->id); add_hash_param(stonith_op->meta, "stonith_action", op?op:data_set->stonith_action); return stonith_op; } /* * Create dependancies for stonith and shutdown operations */ gboolean stage6(pe_working_set_t * data_set) { action_t *dc_down = NULL; action_t *dc_fence = NULL; action_t *stonith_op = NULL; action_t *last_stonith = NULL; gboolean integrity_lost = FALSE; action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); action_t *done = get_pseudo_op(STONITH_DONE, data_set); gboolean need_stonith = FALSE; GListPtr gIter = data_set->nodes; crm_trace("Processing fencing and shutdown cases"); if (is_set(data_set->flags, pe_flag_stonith_enabled) && (is_set(data_set->flags, pe_flag_have_quorum) || data_set->no_quorum_policy == no_quorum_ignore || data_set->no_quorum_policy == no_quorum_suicide)) { need_stonith = TRUE; } if (need_stonith && any_managed_resouces(data_set) == FALSE) { crm_notice("Delaying fencing operations until there are resources to manage"); need_stonith = FALSE; } for (; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; stonith_op = NULL; if (node->details->unclean && need_stonith) { pe_warn("Scheduling Node %s for STONITH", node->details->uname); stonith_op = pe_fence_op(node, NULL, data_set); stonith_constraints(node, stonith_op, data_set); if (node->details->is_dc) { dc_down = stonith_op; dc_fence = stonith_op; } else { if (last_stonith) { order_actions(last_stonith, stonith_op, pe_order_optional); } last_stonith = stonith_op; } } else if (node->details->online && node->details->shutdown) { action_t *down_op = NULL; crm_notice("Scheduling Node %s for shutdown", node->details->uname); down_op = custom_action(NULL, strdup(CRM_OP_SHUTDOWN), CRM_OP_SHUTDOWN, node, FALSE, TRUE, data_set); shutdown_constraints(node, down_op, data_set); add_hash_param(down_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); if (node->details->is_dc) { dc_down = down_op; } } if (node->details->unclean && stonith_op == NULL) { integrity_lost = TRUE; pe_warn("Node %s is unclean!", node->details->uname); } } if (integrity_lost) { if (is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { pe_warn("YOUR RESOURCES ARE NOW LIKELY COMPROMISED"); pe_err("ENABLE STONITH TO KEEP YOUR RESOURCES SAFE"); } else if (is_set(data_set->flags, pe_flag_have_quorum) == FALSE) { crm_notice("Cannot fence unclean nodes until quorum is" " attained (or no-quorum-policy is set to ignore)"); } } if (dc_down != NULL) { GListPtr shutdown_matches = find_actions(data_set->actions, CRM_OP_SHUTDOWN, NULL); crm_trace("Ordering shutdowns before %s on %s (DC)", dc_down->task, dc_down->node->details->uname); add_hash_param(dc_down->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); gIter = shutdown_matches; for (; gIter != NULL; gIter = gIter->next) { action_t *node_stop = (action_t *) gIter->data; if (node_stop->node->details->is_dc) { continue; } crm_debug("Ordering shutdown on %s before %s on %s", node_stop->node->details->uname, dc_down->task, dc_down->node->details->uname); order_actions(node_stop, dc_down, pe_order_optional); } if (last_stonith && dc_down != last_stonith) { order_actions(last_stonith, dc_down, pe_order_optional); } g_list_free(shutdown_matches); } if (last_stonith) { order_actions(last_stonith, done, pe_order_implies_then); } else if (dc_fence) { order_actions(dc_down, done, pe_order_implies_then); } order_actions(done, all_stopped, pe_order_implies_then); return TRUE; } /* * Determin the sets of independant actions and the correct order for the * actions in each set. * * Mark dependencies of un-runnable actions un-runnable * */ static GListPtr find_actions_by_task(GListPtr actions, resource_t * rsc, const char *original_key) { GListPtr list = NULL; list = find_actions(actions, original_key, NULL); if (list == NULL) { /* we're potentially searching a child of the original resource */ char *key = NULL; char *tmp = NULL; char *task = NULL; int interval = 0; if (parse_op_key(original_key, &tmp, &task, &interval)) { key = generate_op_key(rsc->id, task, interval); /* crm_err("looking up %s instead of %s", key, original_key); */ /* slist_iter(action, action_t, actions, lpc, */ /* crm_err(" - %s", action->uuid)); */ list = find_actions(actions, key, NULL); } else { crm_err("search key: %s", original_key); } free(key); free(tmp); free(task); } return list; } static void rsc_order_then(action_t * lh_action, resource_t * rsc, order_constraint_t * order) { GListPtr gIter = NULL; GListPtr rh_actions = NULL; action_t *rh_action = NULL; enum pe_ordering type = order->type; CRM_CHECK(rsc != NULL, return); CRM_CHECK(order != NULL, return); rh_action = order->rh_action; crm_trace("Processing RH of ordering constraint %d", order->id); if (rh_action != NULL) { rh_actions = g_list_prepend(NULL, rh_action); } else if (rsc != NULL) { rh_actions = find_actions_by_task(rsc->actions, rsc, order->rh_action_task); } if (rh_actions == NULL) { pe_rsc_trace(rsc, "No RH-Side (%s/%s) found for constraint..." " ignoring", rsc->id, order->rh_action_task); if (lh_action) { pe_rsc_trace(rsc, "LH-Side was: %s", lh_action->uuid); } return; } if (lh_action && lh_action->rsc == rsc && is_set(lh_action->flags, pe_action_dangle)) { pe_rsc_trace(rsc, "Detected dangling operation %s -> %s", lh_action->uuid, order->rh_action_task); clear_bit(type, pe_order_implies_then); } gIter = rh_actions; for (; gIter != NULL; gIter = gIter->next) { action_t *rh_action_iter = (action_t *) gIter->data; if (lh_action) { order_actions(lh_action, rh_action_iter, type); } else if (type & pe_order_implies_then) { update_action_flags(rh_action_iter, pe_action_runnable | pe_action_clear); crm_warn("Unrunnable %s 0x%.6x", rh_action_iter->uuid, type); } else { crm_warn("neither %s 0x%.6x", rh_action_iter->uuid, type); } } g_list_free(rh_actions); } static void rsc_order_first(resource_t * lh_rsc, order_constraint_t * order, pe_working_set_t * data_set) { GListPtr gIter = NULL; GListPtr lh_actions = NULL; action_t *lh_action = order->lh_action; resource_t *rh_rsc = order->rh_rsc; crm_trace("Processing LH of ordering constraint %d", order->id); CRM_ASSERT(lh_rsc != NULL); if (lh_action != NULL) { lh_actions = g_list_prepend(NULL, lh_action); } else if (lh_action == NULL) { lh_actions = find_actions_by_task(lh_rsc->actions, lh_rsc, order->lh_action_task); } if (lh_actions == NULL && lh_rsc != rh_rsc) { char *key = NULL; char *rsc_id = NULL; char *op_type = NULL; int interval = 0; parse_op_key(order->lh_action_task, &rsc_id, &op_type, &interval); key = generate_op_key(lh_rsc->id, op_type, interval); if (lh_rsc->fns->state(lh_rsc, TRUE) != RSC_ROLE_STOPPED || safe_str_neq(op_type, RSC_STOP)) { pe_rsc_trace(lh_rsc, "No LH-Side (%s/%s) found for constraint %d with %s - creating", lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task); lh_action = custom_action(lh_rsc, key, op_type, NULL, TRUE, TRUE, data_set); lh_actions = g_list_prepend(NULL, lh_action); } else { free(key); pe_rsc_trace(lh_rsc, "No LH-Side (%s/%s) found for constraint %d with %s - ignoring", lh_rsc->id, order->lh_action_task, order->id, order->rh_action_task); } free(op_type); free(rsc_id); } gIter = lh_actions; for (; gIter != NULL; gIter = gIter->next) { action_t *lh_action_iter = (action_t *) gIter->data; if (rh_rsc == NULL && order->rh_action) { rh_rsc = order->rh_action->rsc; } if (rh_rsc) { rsc_order_then(lh_action_iter, rh_rsc, order); } else if (order->rh_action) { order_actions(lh_action_iter, order->rh_action, order->type); } } g_list_free(lh_actions); } extern gboolean update_action(action_t * action); gboolean stage7(pe_working_set_t * data_set) { GListPtr gIter = NULL; crm_trace("Applying ordering constraints"); /* Don't ask me why, but apparently they need to be processed in * the order they were created in... go figure * * Also g_list_prepend() has horrendous performance characteristics * So we need to use g_list_prepend() and then reverse the list here */ data_set->ordering_constraints = g_list_reverse(data_set->ordering_constraints); gIter = data_set->ordering_constraints; for (; gIter != NULL; gIter = gIter->next) { order_constraint_t *order = (order_constraint_t *) gIter->data; resource_t *rsc = order->lh_rsc; crm_trace("Applying ordering constraint: %d", order->id); if (rsc != NULL) { crm_trace("rsc_action-to-*"); rsc_order_first(rsc, order, data_set); continue; } rsc = order->rh_rsc; if (rsc != NULL) { crm_trace("action-to-rsc_action"); rsc_order_then(order->lh_action, rsc, order); } else { crm_trace("action-to-action"); order_actions(order->lh_action, order->rh_action, order->type); } } crm_trace("Updating %d actions", g_list_length(data_set->actions)); gIter = data_set->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; update_action(action); } crm_trace("Processing migrations"); gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; rsc_migrate_reload(rsc, data_set); LogActions(rsc, data_set, FALSE); } return TRUE; } static gint sort_notify_entries(gconstpointer a, gconstpointer b) { int tmp; const notify_entry_t *entry_a = a; const notify_entry_t *entry_b = b; if (entry_a == NULL && entry_b == NULL) { return 0; } if (entry_a == NULL) { return 1; } if (entry_b == NULL) { return -1; } if (entry_a->rsc == NULL && entry_b->rsc == NULL) { return 0; } if (entry_a->rsc == NULL) { return 1; } if (entry_b->rsc == NULL) { return -1; } tmp = strcmp(entry_a->rsc->id, entry_b->rsc->id); if (tmp != 0) { return tmp; } if (entry_a->node == NULL && entry_b->node == NULL) { return 0; } if (entry_a->node == NULL) { return 1; } if (entry_b->node == NULL) { return -1; } return strcmp(entry_a->node->details->id, entry_b->node->details->id); } static void expand_list(GListPtr list, char **rsc_list, char **node_list) { GListPtr gIter = list; const char *uname = NULL; const char *rsc_id = NULL; const char *last_rsc_id = NULL; if (rsc_list) { *rsc_list = NULL; } if (list == NULL) { if (rsc_list) { *rsc_list = strdup(" "); } if (node_list) { *node_list = strdup(" "); } return; } if (node_list) { *node_list = NULL; } for (; gIter != NULL; gIter = gIter->next) { notify_entry_t *entry = (notify_entry_t *) gIter->data; CRM_CHECK(entry != NULL, continue); CRM_CHECK(entry->rsc != NULL, continue); CRM_CHECK(node_list == NULL || entry->node != NULL, continue); uname = NULL; rsc_id = entry->rsc->id; CRM_ASSERT(rsc_id != NULL); /* filter dups */ if (safe_str_eq(rsc_id, last_rsc_id)) { continue; } last_rsc_id = rsc_id; if (rsc_list != NULL) { int existing_len = 0; int len = 2 + strlen(rsc_id); /* +1 space, +1 EOS */ if (rsc_list && *rsc_list) { existing_len = strlen(*rsc_list); } crm_trace("Adding %s (%dc) at offset %d", rsc_id, len - 2, existing_len); *rsc_list = realloc(*rsc_list, len + existing_len); sprintf(*rsc_list + existing_len, "%s ", rsc_id); } if (entry->node != NULL) { uname = entry->node->details->uname; } if (node_list != NULL && uname) { int existing_len = 0; int len = 2 + strlen(uname); if (node_list && *node_list) { existing_len = strlen(*node_list); } crm_trace("Adding %s (%dc) at offset %d", uname, len - 2, existing_len); *node_list = realloc(*node_list, len + existing_len); sprintf(*node_list + existing_len, "%s ", uname); } } } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { add_hash_param(user_data, key, value); } static action_t * pe_notify(resource_t * rsc, node_t * node, action_t * op, action_t * confirm, notify_data_t * n_data, pe_working_set_t * data_set) { char *key = NULL; action_t *trigger = NULL; const char *value = NULL; const char *task = NULL; if (op == NULL || confirm == NULL) { pe_rsc_trace(rsc, "Op=%p confirm=%p", op, confirm); return NULL; } CRM_CHECK(node != NULL, return NULL); if (node->details->online == FALSE) { pe_rsc_trace(rsc, "Skipping notification for %s: node offline", rsc->id); return NULL; } else if (is_set(op->flags, pe_action_runnable) == FALSE) { pe_rsc_trace(rsc, "Skipping notification for %s: not runnable", op->uuid); return NULL; } value = g_hash_table_lookup(op->meta, "notify_type"); task = g_hash_table_lookup(op->meta, "notify_operation"); pe_rsc_trace(rsc, "Creating notify actions for %s: %s (%s-%s)", op->uuid, rsc->id, value, task); key = generate_notify_key(rsc->id, value, task); trigger = custom_action(rsc, key, op->task, node, is_set(op->flags, pe_action_optional), TRUE, data_set); g_hash_table_foreach(op->meta, dup_attr, trigger->meta); g_hash_table_foreach(n_data->keys, dup_attr, trigger->meta); /* pseudo_notify before notify */ pe_rsc_trace(rsc, "Ordering %s before %s (%d->%d)", op->uuid, trigger->uuid, trigger->id, op->id); order_actions(op, trigger, pe_order_optional); order_actions(trigger, confirm, pe_order_optional); return trigger; } static void pe_post_notify(resource_t * rsc, node_t * node, notify_data_t * n_data, pe_working_set_t * data_set) { action_t *notify = NULL; CRM_CHECK(rsc != NULL, return); if (n_data->post == NULL) { return; /* Nothing to do */ } notify = pe_notify(rsc, node, n_data->post, n_data->post_done, n_data, data_set); if (notify != NULL) { notify->priority = INFINITY; } if (n_data->post_done) { GListPtr gIter = rsc->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *mon = (action_t *) gIter->data; const char *interval = g_hash_table_lookup(mon->meta, "interval"); if (interval == NULL || safe_str_eq(interval, "0")) { pe_rsc_trace(rsc, "Skipping %s: interval", mon->uuid); continue; } else if (safe_str_eq(mon->task, "cancel")) { pe_rsc_trace(rsc, "Skipping %s: cancel", mon->uuid); continue; } order_actions(n_data->post_done, mon, pe_order_optional); } } } notify_data_t * create_notification_boundaries(resource_t * rsc, const char *action, action_t * start, action_t * end, pe_working_set_t * data_set) { /* Create the pseudo ops that preceed and follow the actual notifications */ /* * Creates two sequences (conditional on start and end being supplied): * pre_notify -> pre_notify_complete -> start, and * end -> post_notify -> post_notify_complete * * 'start' and 'end' may be the same event or ${X} and ${X}ed as per clones */ char *key = NULL; notify_data_t *n_data = NULL; if (is_not_set(rsc->flags, pe_rsc_notify)) { return NULL; } n_data = calloc(1, sizeof(notify_data_t)); n_data->action = action; n_data->keys = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if (start) { /* create pre-event notification wrappers */ key = generate_notify_key(rsc->id, "pre", start->task); n_data->pre = custom_action(rsc, key, RSC_NOTIFY, NULL, is_set(start->flags, pe_action_optional), TRUE, data_set); update_action_flags(n_data->pre, pe_action_pseudo); update_action_flags(n_data->pre, pe_action_runnable); add_hash_param(n_data->pre->meta, "notify_type", "pre"); add_hash_param(n_data->pre->meta, "notify_operation", n_data->action); add_hash_param(n_data->pre->meta, "notify_key_type", "pre"); add_hash_param(n_data->pre->meta, "notify_key_operation", start->task); /* create pre_notify_complete */ key = generate_notify_key(rsc->id, "confirmed-pre", start->task); n_data->pre_done = custom_action(rsc, key, RSC_NOTIFIED, NULL, is_set(start->flags, pe_action_optional), TRUE, data_set); update_action_flags(n_data->pre_done, pe_action_pseudo); update_action_flags(n_data->pre_done, pe_action_runnable); add_hash_param(n_data->pre_done->meta, "notify_type", "pre"); add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action); add_hash_param(n_data->pre_done->meta, "notify_key_type", "confirmed-pre"); add_hash_param(n_data->pre_done->meta, "notify_key_operation", start->task); order_actions(n_data->pre_done, start, pe_order_optional); order_actions(n_data->pre, n_data->pre_done, pe_order_optional); } if (end) { /* create post-event notification wrappers */ key = generate_notify_key(rsc->id, "post", end->task); n_data->post = custom_action(rsc, key, RSC_NOTIFY, NULL, is_set(end->flags, pe_action_optional), TRUE, data_set); n_data->post->priority = INFINITY; update_action_flags(n_data->post, pe_action_pseudo); if (is_set(end->flags, pe_action_runnable)) { update_action_flags(n_data->post, pe_action_runnable); } else { update_action_flags(n_data->post, pe_action_runnable | pe_action_clear); } add_hash_param(n_data->post->meta, "notify_type", "post"); add_hash_param(n_data->post->meta, "notify_operation", n_data->action); add_hash_param(n_data->post->meta, "notify_key_type", "post"); add_hash_param(n_data->post->meta, "notify_key_operation", end->task); /* create post_notify_complete */ key = generate_notify_key(rsc->id, "confirmed-post", end->task); n_data->post_done = custom_action(rsc, key, RSC_NOTIFIED, NULL, is_set(end->flags, pe_action_optional), TRUE, data_set); n_data->post_done->priority = INFINITY; update_action_flags(n_data->post_done, pe_action_pseudo); if (is_set(end->flags, pe_action_runnable)) { update_action_flags(n_data->post_done, pe_action_runnable); } else { update_action_flags(n_data->post_done, pe_action_runnable | pe_action_clear); } add_hash_param(n_data->post_done->meta, "notify_type", "post"); add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action); add_hash_param(n_data->post_done->meta, "notify_key_type", "confirmed-post"); add_hash_param(n_data->post_done->meta, "notify_key_operation", end->task); order_actions(end, n_data->post, pe_order_implies_then); order_actions(n_data->post, n_data->post_done, pe_order_implies_then); } if (start && end) { order_actions(n_data->pre_done, n_data->post, pe_order_optional); } if (safe_str_eq(action, RSC_STOP)) { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); order_actions(n_data->post_done, all_stopped, pe_order_optional); } return n_data; } void collect_notification_data(resource_t * rsc, gboolean state, gboolean activity, notify_data_t * n_data) { if (rsc->children) { GListPtr gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; collect_notification_data(child, state, activity, n_data); } return; } if (state) { notify_entry_t *entry = NULL; entry = calloc(1, sizeof(notify_entry_t)); entry->rsc = rsc; if (rsc->running_on) { /* we only take the first one */ entry->node = rsc->running_on->data; } pe_rsc_trace(rsc, "%s state: %s", rsc->id, role2text(rsc->role)); switch (rsc->role) { case RSC_ROLE_STOPPED: n_data->inactive = g_list_prepend(n_data->inactive, entry); break; case RSC_ROLE_STARTED: n_data->active = g_list_prepend(n_data->active, entry); break; case RSC_ROLE_SLAVE: n_data->slave = g_list_prepend(n_data->slave, entry); break; case RSC_ROLE_MASTER: n_data->master = g_list_prepend(n_data->master, entry); break; default: crm_err("Unsupported notify role"); free(entry); break; } } if (activity) { notify_entry_t *entry = NULL; enum action_tasks task; GListPtr gIter = rsc->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *op = (action_t *) gIter->data; if (is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) { entry = calloc(1, sizeof(notify_entry_t)); entry->node = op->node; entry->rsc = rsc; task = text2task(op->task); switch (task) { case start_rsc: n_data->start = g_list_prepend(n_data->start, entry); break; case stop_rsc: n_data->stop = g_list_prepend(n_data->stop, entry); break; case action_promote: n_data->promote = g_list_prepend(n_data->promote, entry); break; case action_demote: n_data->demote = g_list_prepend(n_data->demote, entry); break; default: free(entry); break; } } } } } gboolean expand_notification_data(notify_data_t * n_data) { /* Expand the notification entries into a key=value hashtable * This hashtable is later used in action2xml() */ gboolean required = FALSE; char *rsc_list = NULL; char *node_list = NULL; if (n_data->stop) { n_data->stop = g_list_sort(n_data->stop, sort_notify_entries); } expand_list(n_data->stop, &rsc_list, &node_list); if (rsc_list != NULL && safe_str_neq(" ", rsc_list)) { if (safe_str_eq(n_data->action, RSC_STOP)) { required = TRUE; } } g_hash_table_insert(n_data->keys, strdup("notify_stop_resource"), rsc_list); g_hash_table_insert(n_data->keys, strdup("notify_stop_uname"), node_list); if (n_data->start) { n_data->start = g_list_sort(n_data->start, sort_notify_entries); if (rsc_list && safe_str_eq(n_data->action, RSC_START)) { required = TRUE; } } expand_list(n_data->start, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, strdup("notify_start_resource"), rsc_list); g_hash_table_insert(n_data->keys, strdup("notify_start_uname"), node_list); if (n_data->demote) { n_data->demote = g_list_sort(n_data->demote, sort_notify_entries); if (safe_str_eq(n_data->action, RSC_DEMOTE)) { required = TRUE; } } expand_list(n_data->demote, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, strdup("notify_demote_resource"), rsc_list); g_hash_table_insert(n_data->keys, strdup("notify_demote_uname"), node_list); if (n_data->promote) { n_data->promote = g_list_sort(n_data->promote, sort_notify_entries); if (safe_str_eq(n_data->action, RSC_PROMOTE)) { required = TRUE; } } expand_list(n_data->promote, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, strdup("notify_promote_resource"), rsc_list); g_hash_table_insert(n_data->keys, strdup("notify_promote_uname"), node_list); if (n_data->active) { n_data->active = g_list_sort(n_data->active, sort_notify_entries); } expand_list(n_data->active, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, strdup("notify_active_resource"), rsc_list); g_hash_table_insert(n_data->keys, strdup("notify_active_uname"), node_list); if (n_data->slave) { n_data->slave = g_list_sort(n_data->slave, sort_notify_entries); } expand_list(n_data->slave, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, strdup("notify_slave_resource"), rsc_list); g_hash_table_insert(n_data->keys, strdup("notify_slave_uname"), node_list); if (n_data->master) { n_data->master = g_list_sort(n_data->master, sort_notify_entries); } expand_list(n_data->master, &rsc_list, &node_list); g_hash_table_insert(n_data->keys, strdup("notify_master_resource"), rsc_list); g_hash_table_insert(n_data->keys, strdup("notify_master_uname"), node_list); if (n_data->inactive) { n_data->inactive = g_list_sort(n_data->inactive, sort_notify_entries); } expand_list(n_data->inactive, &rsc_list, NULL); g_hash_table_insert(n_data->keys, strdup("notify_inactive_resource"), rsc_list); if (required && n_data->pre) { update_action_flags(n_data->pre, pe_action_optional | pe_action_clear); update_action_flags(n_data->pre_done, pe_action_optional | pe_action_clear); } if (required && n_data->post) { update_action_flags(n_data->post, pe_action_optional | pe_action_clear); update_action_flags(n_data->post_done, pe_action_optional | pe_action_clear); } return required; } void create_notifications(resource_t * rsc, notify_data_t * n_data, pe_working_set_t * data_set) { GListPtr gIter = NULL; action_t *stop = NULL; action_t *start = NULL; enum action_tasks task = text2task(n_data->action); if (rsc->children) { gIter = rsc->children; for (; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; create_notifications(child, n_data, data_set); } return; } /* Copy notification details into standard ops */ gIter = rsc->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *op = (action_t *) gIter->data; if (is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) { enum action_tasks t = text2task(op->task); switch (t) { case start_rsc: case stop_rsc: case action_promote: case action_demote: g_hash_table_foreach(n_data->keys, dup_attr, op->meta); break; default: break; } } } pe_rsc_trace(rsc, "Creating notificaitons for: %s.%s (%s->%s)", n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role)); stop = find_first_action(rsc->actions, NULL, RSC_STOP, NULL); start = find_first_action(rsc->actions, NULL, RSC_START, NULL); /* stop / demote */ if (rsc->role != RSC_ROLE_STOPPED) { if (task == stop_rsc || task == action_demote) { gIter = rsc->running_on; for (; gIter != NULL; gIter = gIter->next) { node_t *current_node = (node_t *) gIter->data; pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set); if (task == action_demote || stop == NULL || is_set(stop->flags, pe_action_optional)) { pe_post_notify(rsc, current_node, n_data, data_set); } } } } /* start / promote */ if (rsc->next_role != RSC_ROLE_STOPPED) { if (rsc->allocated_to == NULL) { pe_proc_err("Next role '%s' but %s is not allocated", role2text(rsc->next_role), rsc->id); } else if (task == start_rsc || task == action_promote) { if (task != start_rsc || start == NULL || is_set(start->flags, pe_action_optional)) { pe_notify(rsc, rsc->allocated_to, n_data->pre, n_data->pre_done, n_data, data_set); } pe_post_notify(rsc, rsc->allocated_to, n_data, data_set); } } } void free_notification_data(notify_data_t * n_data) { if (n_data == NULL) { return; } g_list_free_full(n_data->stop, free); g_list_free_full(n_data->start, free); g_list_free_full(n_data->demote, free); g_list_free_full(n_data->promote, free); g_list_free_full(n_data->master, free); g_list_free_full(n_data->slave, free); g_list_free_full(n_data->active, free); g_list_free_full(n_data->inactive, free); g_hash_table_destroy(n_data->keys); free(n_data); } int transition_id = -1; /* * Create a dependency graph to send to the transitioner (via the CRMd) */ gboolean stage8(pe_working_set_t * data_set) { GListPtr gIter = NULL; const char *value = NULL; transition_id++; crm_trace("Creating transition graph %d.", transition_id); data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH); value = pe_pref(data_set->config_hash, "cluster-delay"); crm_xml_add(data_set->graph, "cluster-delay", value); value = pe_pref(data_set->config_hash, "stonith-timeout"); crm_xml_add(data_set->graph, "stonith-timeout", value); crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY"); if (is_set(data_set->flags, pe_flag_start_failure_fatal)) { crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY"); } else { crm_xml_add(data_set->graph, "failed-start-offset", "1"); } value = pe_pref(data_set->config_hash, "batch-limit"); crm_xml_add(data_set->graph, "batch-limit", value); crm_xml_add_int(data_set->graph, "transition_id", transition_id); value = pe_pref(data_set->config_hash, "migration-limit"); if (crm_int_helper(value, NULL) > 0) { crm_xml_add(data_set->graph, "migration-limit", value); } /* errors... slist_iter(action, action_t, action_list, lpc, if(action->optional == FALSE && action->runnable == FALSE) { print_action("Ignoring", action, TRUE); } ); */ gIter = data_set->resources; for (; gIter != NULL; gIter = gIter->next) { resource_t *rsc = (resource_t *) gIter->data; pe_rsc_trace(rsc, "processing actions for rsc=%s", rsc->id); rsc->cmds->expand(rsc, data_set); } crm_log_xml_trace(data_set->graph, "created resource-driven action list"); /* catch any non-resource specific actions */ crm_trace("processing non-resource actions"); gIter = data_set->actions; for (; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (action->rsc && action->node && action->node->details->shutdown && is_not_set(data_set->flags, pe_flag_maintenance_mode) && is_not_set(action->flags, pe_action_optional) && is_not_set(action->flags, pe_action_runnable) && crm_str_eq(action->task, RSC_STOP, TRUE) ) { /* Eventually we should just ignore the 'fence' case * But for now its the best way to detect (in CTS) when * CIB resource updates are being lost */ crm_crit("Cannot %s node '%s' because of %s:%s%s", action->node->details->unclean?"fence":"shut down", action->node->details->uname, action->rsc->id, is_not_set(action->rsc->flags, pe_rsc_managed)?" unmanaged":" blocked", is_set(action->rsc->flags, pe_rsc_failed)?" failed":""); } graph_element_from_action(action, data_set); } crm_log_xml_trace(data_set->graph, "created generic action list"); crm_trace("Created transition graph %d.", transition_id); return TRUE; } void cleanup_alloc_calculations(pe_working_set_t * data_set) { if (data_set == NULL) { return; } crm_trace("deleting %d order cons: %p", g_list_length(data_set->ordering_constraints), data_set->ordering_constraints); pe_free_ordering(data_set->ordering_constraints); data_set->ordering_constraints = NULL; crm_trace("deleting %d node cons: %p", g_list_length(data_set->placement_constraints), data_set->placement_constraints); pe_free_rsc_to_node(data_set->placement_constraints); data_set->placement_constraints = NULL; crm_trace("deleting %d inter-resource cons: %p", g_list_length(data_set->colocation_constraints), data_set->colocation_constraints); g_list_free_full(data_set->colocation_constraints, free); data_set->colocation_constraints = NULL; crm_trace("deleting %d ticket deps: %p", g_list_length(data_set->ticket_constraints), data_set->ticket_constraints); g_list_free_full(data_set->ticket_constraints, free); data_set->ticket_constraints = NULL; cleanup_calculations(data_set); } diff --git a/pengine/native.c b/pengine/native.c index b09e02e1df..3f421ef427 100644 --- a/pengine/native.c +++ b/pengine/native.c @@ -1,3156 +1,3159 @@ /* * 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 -#define DELETE_THEN_REFRESH 1 /* The crmd will remove the resource from the CIB itself, making this redundant */ +/* #define DELETE_THEN_REFRESH 1 // The crmd will remove the resource from the CIB itself, making this redundant */ #define INFINITY_HACK (INFINITY * -100) #define VARIANT_NATIVE 1 #include void native_rsc_colocation_rh_must(resource_t * rsc_lh, gboolean update_lh, resource_t * rsc_rh, gboolean update_rh); void native_rsc_colocation_rh_mustnot(resource_t * rsc_lh, gboolean update_lh, resource_t * rsc_rh, gboolean update_rh); void Recurring(resource_t * rsc, action_t * start, node_t * node, pe_working_set_t * data_set); void RecurringOp(resource_t * rsc, action_t * start, node_t * node, xmlNode * operation, pe_working_set_t * data_set); void Recurring_Stopped(resource_t * rsc, action_t * start, node_t * node, pe_working_set_t * data_set); void RecurringOp_Stopped(resource_t * rsc, action_t * start, node_t * node, xmlNode * operation, pe_working_set_t * data_set); void pe_post_notify(resource_t * rsc, node_t * node, action_t * op, notify_data_t * n_data, pe_working_set_t * data_set); gboolean DeleteRsc(resource_t * rsc, node_t * node, gboolean optional, pe_working_set_t * data_set); gboolean StopRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set); gboolean StartRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set); gboolean DemoteRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set); gboolean PromoteRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set); gboolean RoleError(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set); gboolean NullOp(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set); /* *INDENT-OFF* */ enum rsc_role_e rsc_state_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX] = { /* Current State */ /* Next State: Unknown Stopped Started Slave Master */ /* Unknown */ { RSC_ROLE_UNKNOWN, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, }, /* Stopped */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE, RSC_ROLE_SLAVE, }, /* Started */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STARTED, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, /* Slave */ { RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_STOPPED, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, /* Master */ { RSC_ROLE_STOPPED, RSC_ROLE_SLAVE, RSC_ROLE_SLAVE, RSC_ROLE_SLAVE, RSC_ROLE_MASTER, }, }; gboolean (*rsc_action_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX])(resource_t*,node_t*,gboolean,pe_working_set_t*) = { /* Current State */ /* Next State: Unknown Stopped Started Slave Master */ /* Unknown */ { RoleError, StopRsc, RoleError, RoleError, RoleError, }, /* Stopped */ { RoleError, NullOp, StartRsc, StartRsc, RoleError, }, /* Started */ { RoleError, StopRsc, NullOp, NullOp, PromoteRsc, }, /* Slave */ { RoleError, StopRsc, StopRsc, NullOp, PromoteRsc, }, /* Master */ { RoleError, DemoteRsc, DemoteRsc, DemoteRsc, NullOp, }, }; /* *INDENT-ON* */ struct capacity_data { node_t *node; resource_t *rsc; gboolean is_enough; }; static void check_capacity(gpointer key, gpointer value, gpointer user_data) { int required = 0; int remaining = 0; struct capacity_data *data = user_data; required = crm_parse_int(value, "0"); remaining = crm_parse_int(g_hash_table_lookup(data->node->details->utilization, key), "0"); if (required > remaining) { pe_rsc_debug(data->rsc, "Node %s has no enough %s for resource %s: required=%d remaining=%d", data->node->details->uname, (char *)key, data->rsc->id, required, remaining); data->is_enough = FALSE; } } static gboolean have_enough_capacity(node_t * node, resource_t * rsc) { struct capacity_data data; data.node = node; data.rsc = rsc; data.is_enough = TRUE; g_hash_table_foreach(rsc->utilization, check_capacity, &data); return data.is_enough; } static gboolean native_choose_node(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set) { /* 1. Sort by weight 2. color.chosen_node = the node (of those with the highest wieght) with the fewest resources 3. remove color.chosen_node from all other colors */ int alloc_details = scores_log_level + 1; GListPtr nodes = NULL; node_t *chosen = NULL; int lpc = 0; int multiple = 0; int length = 0; gboolean result = FALSE; if (safe_str_neq(data_set->placement_strategy, "default")) { GListPtr gIter = NULL; for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; if (have_enough_capacity(node, rsc) == FALSE) { pe_rsc_debug(rsc, "Resource %s cannot be allocated to node %s: none of enough capacity", rsc->id, node->details->uname); resource_location(rsc, node, -INFINITY, "__limit_utilization_", data_set); } } dump_node_scores(alloc_details, rsc, "Post-utilization", rsc->allowed_nodes); } length = g_hash_table_size(rsc->allowed_nodes); if (is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to ? TRUE : FALSE; } if (prefer) { chosen = g_hash_table_lookup(rsc->allowed_nodes, prefer->details->id); if (chosen && chosen->weight >= 0 && can_run_resources(chosen)) { pe_rsc_trace(rsc, "Using preferred node %s for %s instead of choosing from %d candidates", chosen->details->uname, rsc->id, length); } else if (chosen && chosen->weight < 0) { pe_rsc_trace(rsc, "Preferred node %s for %s was unavailable", chosen->details->uname, rsc->id); chosen = NULL; } else if (chosen && can_run_resources(chosen)) { pe_rsc_trace(rsc, "Preferred node %s for %s was unsuitable", chosen->details->uname, rsc->id); chosen = NULL; } else { pe_rsc_trace(rsc, "Preferred node %s for %s was unknown", prefer->details->uname, rsc->id); } } if (chosen == NULL && rsc->allowed_nodes) { nodes = g_hash_table_get_values(rsc->allowed_nodes); nodes = g_list_sort_with_data(nodes, sort_node_weight, g_list_nth_data(rsc->running_on, 0)); chosen = g_list_nth_data(nodes, 0); pe_rsc_trace(rsc, "Chose node %s for %s from %d candidates", chosen ? chosen->details->uname : "", rsc->id, length); if (chosen && chosen->weight > 0 && can_run_resources(chosen)) { node_t *running = g_list_nth_data(rsc->running_on, 0); if (running && can_run_resources(running) == FALSE) { pe_rsc_trace(rsc, "Current node for %s (%s) can't run resources", rsc->id, running->details->uname); running = NULL; } for (lpc = 1; lpc < length && running; lpc++) { node_t *tmp = g_list_nth_data(nodes, lpc); if (tmp->weight == chosen->weight) { multiple++; if (tmp->details == running->details) { /* prefer the existing node if scores are equal */ chosen = tmp; } } } } } if (multiple > 1) { int log_level = LOG_INFO; char *score = score2char(chosen->weight); if (chosen->weight >= INFINITY) { log_level = LOG_WARNING; } do_crm_log(log_level, "%d nodes with equal score (%s) for" " running %s resources. Chose %s.", multiple, score, rsc->id, chosen->details->uname); free(score); } result = native_assign_node(rsc, nodes, chosen, FALSE); g_list_free(nodes); return result; } static int node_list_attr_score(GHashTable * list, const char *attr, const char *value) { GHashTableIter iter; node_t *node = NULL; int best_score = -INFINITY; const char *best_node = NULL; if (attr == NULL) { attr = "#" XML_ATTR_UNAME; } g_hash_table_iter_init(&iter, list); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { int weight = node->weight; if (can_run_resources(node) == FALSE) { weight = -INFINITY; } if (weight > best_score || best_node == NULL) { const char *tmp = g_hash_table_lookup(node->details->attrs, attr); if (safe_str_eq(value, tmp)) { best_score = weight; best_node = node->details->uname; } } } if (safe_str_neq(attr, "#" XML_ATTR_UNAME)) { crm_info("Best score for %s=%s was %s with %d", attr, value, best_node ? best_node : "", best_score); } return best_score; } static void node_hash_update(GHashTable * list1, GHashTable * list2, const char *attr, float factor, gboolean only_positive) { int score = 0; int new_score = 0; GHashTableIter iter; node_t *node = NULL; if (attr == NULL) { attr = "#" XML_ATTR_UNAME; } g_hash_table_iter_init(&iter, list1); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { CRM_CHECK(node != NULL, continue); score = node_list_attr_score(list2, attr, g_hash_table_lookup(node->details->attrs, attr)); new_score = merge_weights(factor * score, node->weight); if (factor < 0 && score < 0) { /* Negative preference for a node with a negative score * should not become a positive preference * * TODO - Decide if we want to filter only if weight == -INFINITY * */ crm_trace("%s: Filtering %d + %f*%d (factor * score)", node->details->uname, node->weight, factor, score); } else if (node->weight == INFINITY_HACK) { crm_trace("%s: Filtering %d + %f*%d (node < 0)", node->details->uname, node->weight, factor, score); } else if (only_positive && new_score < 0 && node->weight > 0) { node->weight = INFINITY_HACK; crm_trace("%s: Filtering %d + %f*%d (score > 0)", node->details->uname, node->weight, factor, score); } else if (only_positive && new_score < 0 && node->weight == 0) { crm_trace("%s: Filtering %d + %f*%d (score == 0)", node->details->uname, node->weight, factor, score); } else { crm_trace("%s: %d + %f*%d", node->details->uname, node->weight, factor, score); node->weight = new_score; } } } static GHashTable * node_hash_dup(GHashTable * hash) { /* Hack! */ GListPtr list = g_hash_table_get_values(hash); GHashTable *result = node_hash_from_list(list); g_list_free(list); return result; } GHashTable * native_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr, float factor, enum pe_weights flags) { return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags); } GHashTable * rsc_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr, float factor, enum pe_weights flags) { GHashTable *work = NULL; int multiplier = 1; if (factor < 0) { multiplier = -1; } if (is_set(rsc->flags, pe_rsc_merging)) { pe_rsc_info(rsc, "%s: Breaking dependency loop at %s", rhs, rsc->id); return nodes; } set_bit(rsc->flags, pe_rsc_merging); if (is_set(flags, pe_weights_init)) { if (rsc->variant == pe_group && rsc->children) { GListPtr last = rsc->children; while (last->next != NULL) { last = last->next; } pe_rsc_trace(rsc, "Merging %s as a group %p %p", rsc->id, rsc->children, last); work = rsc_merge_weights(last->data, rhs, NULL, attr, factor, flags); } else { work = node_hash_dup(rsc->allowed_nodes); } clear_bit(flags, pe_weights_init); } else { pe_rsc_trace(rsc, "%s: Combining scores from %s", rhs, rsc->id); work = node_hash_dup(nodes); node_hash_update(work, rsc->allowed_nodes, attr, factor, is_set(flags, pe_weights_positive)); } if (is_set(flags, pe_weights_rollback) && can_run_any(work) == FALSE) { pe_rsc_info(rsc, "%s: Rolling back scores from %s", rhs, rsc->id); g_hash_table_destroy(work); clear_bit(rsc->flags, pe_rsc_merging); return nodes; } if (can_run_any(work)) { GListPtr gIter = NULL; if (is_set(flags, pe_weights_forward)) { gIter = rsc->rsc_cons; } else { gIter = rsc->rsc_cons_lhs; } for (; gIter != NULL; gIter = gIter->next) { resource_t *other = NULL; rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; if (is_set(flags, pe_weights_forward)) { other = constraint->rsc_rh; } else { other = constraint->rsc_lh; } pe_rsc_trace(rsc, "Applying %s (%s)", constraint->id, other->id); work = rsc_merge_weights(other, rhs, work, constraint->node_attribute, multiplier * (float) constraint->score / INFINITY, flags); dump_node_scores(LOG_TRACE, NULL, rhs, work); } } if(is_set(flags, pe_weights_positive)) { node_t *node = NULL; GHashTableIter iter; g_hash_table_iter_init(&iter, work); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { if (node->weight == INFINITY_HACK) { node->weight = 1; } } } if (nodes) { g_hash_table_destroy(nodes); } clear_bit(rsc->flags, pe_rsc_merging); return work; } node_t * native_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set) { GListPtr gIter = NULL; int alloc_details = scores_log_level + 1; if (rsc->parent && is_not_set(rsc->parent->flags, pe_rsc_allocating)) { /* never allocate children on their own */ pe_rsc_debug(rsc, "Escalating allocation of %s to its parent: %s", rsc->id, rsc->parent->id); rsc->parent->cmds->allocate(rsc->parent, prefer, data_set); } if (is_not_set(rsc->flags, pe_rsc_provisional)) { return rsc->allocated_to; } if (is_set(rsc->flags, pe_rsc_allocating)) { pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id); return NULL; } set_bit(rsc->flags, pe_rsc_allocating); print_resource(alloc_details, "Allocating: ", rsc, FALSE); dump_node_scores(alloc_details, rsc, "Pre-allloc", rsc->allowed_nodes); for (gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; GHashTable *archive = NULL; resource_t *rsc_rh = constraint->rsc_rh; pe_rsc_trace(rsc, "%s: Pre-Processing %s (%s, %d, %s)", rsc->id, constraint->id, rsc_rh->id, constraint->score, role2text(constraint->role_lh)); if (constraint->role_lh >= RSC_ROLE_MASTER || (constraint->score < 0 && constraint->score > -INFINITY)) { archive = node_hash_dup(rsc->allowed_nodes); } rsc_rh->cmds->allocate(rsc_rh, NULL, data_set); rsc->cmds->rsc_colocation_lh(rsc, rsc_rh, constraint); if (archive && can_run_any(rsc->allowed_nodes) == FALSE) { pe_rsc_info(rsc, "%s: Rolling back scores from %s", rsc->id, rsc_rh->id); g_hash_table_destroy(rsc->allowed_nodes); rsc->allowed_nodes = archive; archive = NULL; } if (archive) { g_hash_table_destroy(archive); } } dump_node_scores(alloc_details, rsc, "Post-coloc", rsc->allowed_nodes); for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; rsc->allowed_nodes = constraint->rsc_lh->cmds->merge_weights(constraint->rsc_lh, rsc->id, rsc->allowed_nodes, constraint->node_attribute, (float) constraint->score / INFINITY, pe_weights_rollback); } for (gIter = rsc->rsc_tickets; gIter != NULL; gIter = gIter->next) { rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) gIter->data; if (rsc_ticket->ticket->granted == FALSE || rsc_ticket->ticket->standby) { rsc_ticket_constraint(rsc, rsc_ticket, data_set); } } print_resource(LOG_DEBUG_2, "Allocating: ", rsc, FALSE); if (rsc->next_role == RSC_ROLE_STOPPED) { pe_rsc_trace(rsc, "Making sure %s doesn't get allocated", rsc->id); /* make sure it doesnt come up again */ resource_location(rsc, NULL, -INFINITY, XML_RSC_ATTR_TARGET_ROLE, data_set); } dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes); if (is_set(data_set->flags, pe_flag_stonith_enabled) && is_set(data_set->flags, pe_flag_have_stonith_resource) == FALSE) { clear_bit(rsc->flags, pe_rsc_managed); } if (is_not_set(rsc->flags, pe_rsc_managed)) { const char *reason = NULL; node_t *assign_to = NULL; rsc->next_role = rsc->role; if (rsc->running_on == NULL) { reason = "inactive"; } else if (rsc->role == RSC_ROLE_MASTER) { assign_to = rsc->running_on->data; reason = "master"; } else if (is_set(rsc->flags, pe_rsc_failed)) { reason = "failed"; } else { assign_to = rsc->running_on->data; reason = "active"; } pe_rsc_info(rsc, "Unmanaged resource %s allocated to %s: %s", rsc->id, assign_to ? assign_to->details->uname : "'nowhere'", reason); native_assign_node(rsc, NULL, assign_to, TRUE); } else if (is_set(data_set->flags, pe_flag_stop_everything)) { pe_rsc_debug(rsc, "Forcing %s to stop", rsc->id); native_assign_node(rsc, NULL, NULL, TRUE); } else if (is_set(rsc->flags, pe_rsc_provisional) && native_choose_node(rsc, prefer, data_set)) { pe_rsc_trace(rsc, "Allocated resource %s to %s", rsc->id, rsc->allocated_to->details->uname); } else if (rsc->allocated_to == NULL) { if (is_not_set(rsc->flags, pe_rsc_orphan)) { pe_rsc_info(rsc, "Resource %s cannot run anywhere", rsc->id); } else if (rsc->running_on != NULL) { pe_rsc_info(rsc, "Stopping orphan resource %s", rsc->id); } } else { pe_rsc_debug(rsc, "Pre-Allocated resource %s to %s", rsc->id, rsc->allocated_to->details->uname); } clear_bit(rsc->flags, pe_rsc_allocating); print_resource(LOG_DEBUG_3, "Allocated ", rsc, TRUE); return rsc->allocated_to; } static gboolean is_op_dup(resource_t * rsc, const char *name, const char *interval) { gboolean dup = FALSE; const char *id = NULL; const char *value = NULL; xmlNode *operation = NULL; for (operation = __xml_first_child(rsc->ops_xml); operation != NULL; operation = __xml_next(operation)) { if (crm_str_eq((const char *)operation->name, "op", TRUE)) { value = crm_element_value(operation, "name"); if (safe_str_neq(value, name)) { continue; } value = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); if (value == NULL) { value = "0"; } if (safe_str_neq(value, interval)) { continue; } if (id == NULL) { id = ID(operation); } else { crm_config_err("Operation %s is a duplicate of %s", ID(operation), id); crm_config_err ("Do not use the same (name, interval) combination more than once per resource"); dup = TRUE; } } } return dup; } void RecurringOp(resource_t * rsc, action_t * start, node_t * node, xmlNode * operation, pe_working_set_t * data_set) { char *key = NULL; const char *name = NULL; const char *value = NULL; const char *interval = NULL; const char *node_uname = NULL; unsigned long long interval_ms = 0; action_t *mon = NULL; gboolean is_optional = TRUE; GListPtr possible_matches = NULL; /* Only process for the operations without role="Stopped" */ value = crm_element_value(operation, "role"); if (value && text2role(value) == RSC_ROLE_STOPPED) { return; } pe_rsc_trace(rsc, "Creating recurring action %s for %s in role %s on %s", ID(operation), rsc->id, role2text(rsc->next_role), node ? node->details->uname : "n/a"); if (node != NULL) { node_uname = node->details->uname; } interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); interval_ms = crm_get_interval(interval); if (interval_ms == 0) { return; } name = crm_element_value(operation, "name"); if (is_op_dup(rsc, name, interval)) { return; } if (safe_str_eq(name, RSC_STOP) || safe_str_eq(name, RSC_START) || safe_str_eq(name, RSC_DEMOTE) || safe_str_eq(name, RSC_PROMOTE) ) { crm_config_err("Invalid recurring action %s wth name: '%s'", ID(operation), name); return; } key = generate_op_key(rsc->id, name, interval_ms); if (find_rsc_op_entry(rsc, key) == NULL) { /* disabled */ free(key); return; } if (start != NULL) { pe_rsc_trace(rsc, "Marking %s %s due to %s", key, is_set(start->flags, pe_action_optional) ? "optional" : "manditory", start->uuid); is_optional = (rsc->cmds->action_flags(start, NULL) & pe_action_optional); } else { pe_rsc_trace(rsc, "Marking %s optional", key); is_optional = TRUE; } /* start a monitor for an already active resource */ possible_matches = find_actions_exact(rsc->actions, key, node); if (possible_matches == NULL) { is_optional = FALSE; pe_rsc_trace(rsc, "Marking %s manditory: not active", key); } else { g_list_free(possible_matches); } if ((rsc->next_role == RSC_ROLE_MASTER && value == NULL) || (value != NULL && text2role(value) != rsc->next_role)) { int log_level = LOG_DEBUG_2; const char *result = "Ignoring"; if (is_optional) { char *local_key = strdup(key); log_level = LOG_INFO; result = "Cancelling"; /* its running : cancel it */ mon = custom_action(rsc, local_key, RSC_CANCEL, node, FALSE, TRUE, data_set); free(mon->task); mon->task = strdup(RSC_CANCEL); add_hash_param(mon->meta, XML_LRM_ATTR_INTERVAL, interval); add_hash_param(mon->meta, XML_LRM_ATTR_TASK, name); local_key = NULL; switch (rsc->role) { case RSC_ROLE_SLAVE: case RSC_ROLE_STARTED: if (rsc->next_role == RSC_ROLE_MASTER) { local_key = promote_key(rsc); } else if (rsc->next_role == RSC_ROLE_STOPPED) { local_key = stop_key(rsc); } break; case RSC_ROLE_MASTER: local_key = demote_key(rsc); break; default: break; } if (local_key) { custom_action_order(rsc, NULL, mon, rsc, local_key, NULL, pe_order_runnable_left, data_set); } mon = NULL; } do_crm_log(log_level, "%s action %s (%s vs. %s)", result, key, value ? value : role2text(RSC_ROLE_SLAVE), role2text(rsc->next_role)); free(key); key = NULL; return; } mon = custom_action(rsc, key, name, node, is_optional, TRUE, data_set); key = mon->uuid; if (is_optional) { pe_rsc_trace(rsc, "%s\t %s (optional)", crm_str(node_uname), mon->uuid); } if (start == NULL || is_set(start->flags, pe_action_runnable) == FALSE) { pe_rsc_debug(rsc, "%s\t %s (cancelled : start un-runnable)", crm_str(node_uname), mon->uuid); update_action_flags(mon, pe_action_runnable | pe_action_clear); } else if (node == NULL || node->details->online == FALSE || node->details->unclean) { pe_rsc_debug(rsc, "%s\t %s (cancelled : no node available)", crm_str(node_uname), mon->uuid); update_action_flags(mon, pe_action_runnable | pe_action_clear); } else if (is_set(mon->flags, pe_action_optional) == FALSE) { pe_rsc_info(rsc, " Start recurring %s (%llus) for %s on %s", mon->task, interval_ms / 1000, rsc->id, crm_str(node_uname)); } if (rsc->next_role == RSC_ROLE_MASTER) { char *running_master = crm_itoa(PCMK_EXECRA_RUNNING_MASTER); add_hash_param(mon->meta, XML_ATTR_TE_TARGET_RC, running_master); free(running_master); } if (node == NULL || is_set(rsc->flags, pe_rsc_managed)) { custom_action_order(rsc, start_key(rsc), NULL, NULL, strdup(key), mon, pe_order_implies_then | pe_order_runnable_left, data_set); if (rsc->next_role == RSC_ROLE_MASTER) { custom_action_order(rsc, promote_key(rsc), NULL, rsc, NULL, mon, pe_order_optional | pe_order_runnable_left, data_set); } else if (rsc->role == RSC_ROLE_MASTER) { custom_action_order(rsc, demote_key(rsc), NULL, rsc, NULL, mon, pe_order_optional | pe_order_runnable_left, data_set); } } } void Recurring(resource_t * rsc, action_t * start, node_t * node, pe_working_set_t * data_set) { if (is_not_set(data_set->flags, pe_flag_maintenance_mode)) { xmlNode *operation = NULL; for (operation = __xml_first_child(rsc->ops_xml); operation != NULL; operation = __xml_next(operation)) { if (crm_str_eq((const char *)operation->name, "op", TRUE)) { RecurringOp(rsc, start, node, operation, data_set); } } } } void RecurringOp_Stopped(resource_t * rsc, action_t * start, node_t * node, xmlNode * operation, pe_working_set_t * data_set) { char *key = NULL; const char *name = NULL; const char *role = NULL; const char *interval = NULL; const char *node_uname = NULL; unsigned long long interval_ms = 0; GListPtr possible_matches = NULL; GListPtr gIter = NULL; /* TODO: Support of non-unique clone */ if (is_set(rsc->flags, pe_rsc_unique) == FALSE) { return; } /* Only process for the operations with role="Stopped" */ role = crm_element_value(operation, "role"); if (role == NULL || text2role(role) != RSC_ROLE_STOPPED) { return; } pe_rsc_trace(rsc, "Creating recurring actions %s for %s in role %s on nodes where it'll not be running", ID(operation), rsc->id, role2text(rsc->next_role)); if (node != NULL) { node_uname = node->details->uname; } interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); interval_ms = crm_get_interval(interval); if (interval_ms == 0) { return; } name = crm_element_value(operation, "name"); if (is_op_dup(rsc, name, interval)) { return; } if (safe_str_eq(name, RSC_STOP) || safe_str_eq(name, RSC_START) || safe_str_eq(name, RSC_DEMOTE) || safe_str_eq(name, RSC_PROMOTE) ) { crm_config_err("Invalid recurring action %s wth name: '%s'", ID(operation), name); return; } key = generate_op_key(rsc->id, name, interval_ms); if (find_rsc_op_entry(rsc, key) == NULL) { /* disabled */ free(key); return; } /* if the monitor exists on the node where the resource will be running, cancel it */ if (node != NULL) { possible_matches = find_actions_exact(rsc->actions, key, node); if (possible_matches) { action_t *cancel_op = NULL; char *local_key = strdup(key); g_list_free(possible_matches); cancel_op = custom_action(rsc, local_key, RSC_CANCEL, node, FALSE, TRUE, data_set); free(cancel_op->task); cancel_op->task = strdup(RSC_CANCEL); add_hash_param(cancel_op->meta, XML_LRM_ATTR_INTERVAL, interval); add_hash_param(cancel_op->meta, XML_LRM_ATTR_TASK, name); local_key = NULL; if (rsc->next_role == RSC_ROLE_STARTED || rsc->next_role == RSC_ROLE_SLAVE) { /* rsc->role == RSC_ROLE_STOPPED: cancel the monitor before start */ /* rsc->role == RSC_ROLE_STARTED: for a migration, cancel the monitor on the target node before start */ custom_action_order(rsc, NULL, cancel_op, rsc, start_key(rsc), NULL, pe_order_runnable_left, data_set); } pe_rsc_info(rsc, "Cancel action %s (%s vs. %s) on %s", key, role, role2text(rsc->next_role), crm_str(node_uname)); } } for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) { node_t *stop_node = (node_t *) gIter->data; const char *stop_node_uname = stop_node->details->uname; gboolean is_optional = TRUE; gboolean probe_is_optional = TRUE; gboolean stop_is_optional = TRUE; action_t *stopped_mon = NULL; char *rc_inactive = NULL; GListPtr probe_complete_ops = NULL; GListPtr stop_ops = NULL; GListPtr local_gIter = NULL; char *stop_op_key = NULL; if (node_uname && safe_str_eq(stop_node_uname, node_uname)) { continue; } pe_rsc_trace(rsc, "Creating recurring action %s for %s on %s", ID(operation), rsc->id, crm_str(stop_node_uname)); /* start a monitor for an already stopped resource */ possible_matches = find_actions_exact(rsc->actions, key, stop_node); if (possible_matches == NULL) { pe_rsc_trace(rsc, "Marking %s manditory on %s: not active", key, crm_str(stop_node_uname)); is_optional = FALSE; } else { pe_rsc_trace(rsc, "Marking %s optional on %s: already active", key, crm_str(stop_node_uname)); is_optional = TRUE; g_list_free(possible_matches); } stopped_mon = custom_action(rsc, strdup(key), name, stop_node, is_optional, TRUE, data_set); rc_inactive = crm_itoa(PCMK_EXECRA_NOT_RUNNING); add_hash_param(stopped_mon->meta, XML_ATTR_TE_TARGET_RC, rc_inactive); free(rc_inactive); probe_complete_ops = find_actions(data_set->actions, CRM_OP_PROBED, NULL); for (local_gIter = probe_complete_ops; local_gIter != NULL; local_gIter = local_gIter->next) { action_t *probe_complete = (action_t *) local_gIter->data; if (probe_complete->node == NULL) { if (is_set(probe_complete->flags, pe_action_optional) == FALSE) { probe_is_optional = FALSE; } if (is_set(probe_complete->flags, pe_action_runnable) == FALSE) { crm_debug("%s\t %s (cancelled : probe un-runnable)", crm_str(stop_node_uname), stopped_mon->uuid); update_action_flags(stopped_mon, pe_action_runnable | pe_action_clear); } if (is_set(rsc->flags, pe_rsc_managed)) { custom_action_order(NULL, NULL, probe_complete, NULL, strdup(key), stopped_mon, pe_order_optional, data_set); } break; } } if (probe_complete_ops) { g_list_free(probe_complete_ops); } stop_op_key = stop_key(rsc); stop_ops = find_actions_exact(rsc->actions, stop_op_key, stop_node); for (local_gIter = stop_ops; local_gIter != NULL; local_gIter = local_gIter->next) { action_t *stop = (action_t *) local_gIter->data; if (is_set(stop->flags, pe_action_optional) == FALSE) { stop_is_optional = FALSE; } if (is_set(stop->flags, pe_action_runnable) == FALSE) { crm_debug("%s\t %s (cancelled : stop un-runnable)", crm_str(stop_node_uname), stopped_mon->uuid); update_action_flags(stopped_mon, pe_action_runnable | pe_action_clear); } if (is_set(rsc->flags, pe_rsc_managed)) { custom_action_order(rsc, strdup(stop_op_key), stop, NULL, strdup(key), stopped_mon, pe_order_implies_then | pe_order_runnable_left, data_set); } } if (stop_ops) { g_list_free(stop_ops); } free(stop_op_key); if (is_optional == FALSE && probe_is_optional && stop_is_optional && is_set(rsc->flags, pe_rsc_managed) == FALSE) { pe_rsc_trace(rsc, "Marking %s optional on %s due to unmanaged", key, crm_str(stop_node_uname)); update_action_flags(stopped_mon, pe_action_optional); } if (is_set(stopped_mon->flags, pe_action_optional)) { pe_rsc_trace(rsc, "%s\t %s (optional)", crm_str(stop_node_uname), stopped_mon->uuid); } if (stop_node->details->online == FALSE || stop_node->details->unclean) { pe_rsc_debug(rsc, "%s\t %s (cancelled : no node available)", crm_str(stop_node_uname), stopped_mon->uuid); update_action_flags(stopped_mon, pe_action_runnable | pe_action_clear); } if (is_set(stopped_mon->flags, pe_action_runnable) && is_set(stopped_mon->flags, pe_action_optional) == FALSE) { crm_notice(" Start recurring %s (%llus) for %s on %s", stopped_mon->task, interval_ms / 1000, rsc->id, crm_str(stop_node_uname)); } } free(key); } void Recurring_Stopped(resource_t * rsc, action_t * start, node_t * node, pe_working_set_t * data_set) { if (is_not_set(data_set->flags, pe_flag_maintenance_mode)) { xmlNode *operation = NULL; for (operation = __xml_first_child(rsc->ops_xml); operation != NULL; operation = __xml_next(operation)) { if (crm_str_eq((const char *)operation->name, "op", TRUE)) { RecurringOp_Stopped(rsc, start, node, operation, data_set); } } } } void native_create_actions(resource_t * rsc, pe_working_set_t * data_set) { action_t *start = NULL; node_t *chosen = NULL; node_t *current = NULL; gboolean need_stop = FALSE; GListPtr gIter = NULL; int num_active_nodes = 0; enum rsc_role_e role = RSC_ROLE_UNKNOWN; enum rsc_role_e next_role = RSC_ROLE_UNKNOWN; chosen = rsc->allocated_to; if (chosen != NULL && rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_STARTED; pe_rsc_trace(rsc, "Fixed next_role: unknown -> %s", role2text(rsc->next_role)); } else if (rsc->next_role == RSC_ROLE_UNKNOWN) { rsc->next_role = RSC_ROLE_STOPPED; pe_rsc_trace(rsc, "Fixed next_role: unknown -> %s", role2text(rsc->next_role)); } pe_rsc_trace(rsc, "Processing state transition for %s %p: %s->%s", rsc->id, rsc, role2text(rsc->role), role2text(rsc->next_role)); if(rsc->running_on) { current = rsc->running_on->data; } for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { /* node_t *n = (node_t *) gIter->data; */ num_active_nodes++; } get_rsc_attributes(rsc->parameters, rsc, chosen, data_set); for (gIter = rsc->dangling_migrations; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t *) gIter->data; action_t *stop = stop_action(rsc, current, FALSE); set_bit(stop->flags, pe_action_dangle); pe_rsc_trace(rsc, "Forcing a cleanup of %s on %s", rsc->id, current->details->uname); if (is_set(data_set->flags, pe_flag_remove_after_stop)) { DeleteRsc(rsc, current, FALSE, data_set); } } if (num_active_nodes > 1) { if (num_active_nodes == 2 && chosen && rsc->partial_migration_target && (chosen->details == rsc->partial_migration_target->details)) { /* Here the chosen node is still the migration target from a partial * migration. Attempt to continue the migration instead of recovering * by stopping the resource everywhere and starting it on a single node. */ pe_rsc_trace(rsc, "Will attempt to continue with a partial migration to target %s from %s", rsc->partial_migration_target->details->id, rsc->partial_migration_source->details->id); } else { const char *type = crm_element_value(rsc->xml, XML_ATTR_TYPE); const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); pe_proc_err("Resource %s (%s::%s) is active on %d nodes %s", rsc->id, class, type, num_active_nodes, recovery2text(rsc->recovery_type)); crm_warn("See %s for more information.", "http://clusterlabs.org/wiki/FAQ#Resource_is_Too_Active"); if (rsc->recovery_type == recovery_stop_start) { need_stop = TRUE; } /* If by chance a partial migration is in process, * but the migration target is not chosen still, clear all * partial migration data. */ rsc->partial_migration_source = rsc->partial_migration_target = NULL; } } if (is_set(rsc->flags, pe_rsc_start_pending)) { start = start_action(rsc, chosen, TRUE); set_bit(start->flags, pe_action_print_always); } if(current && chosen && current->details != chosen->details) { pe_rsc_trace(rsc, "Moving %s", rsc->id); need_stop = TRUE; } else if(is_set(rsc->flags, pe_rsc_failed)) { pe_rsc_trace(rsc, "Recovering %s", rsc->id); need_stop = TRUE; } else if(rsc->role > RSC_ROLE_STARTED && current != NULL && chosen != NULL) { /* Recovery of a promoted resource */ start = start_action(rsc, chosen, TRUE); if(is_set(start->flags, pe_action_optional) == FALSE) { pe_rsc_trace(rsc, "Forced start %s", rsc->id); need_stop = TRUE; } } pe_rsc_trace(rsc, "Creating actions for %s: %s->%s", rsc->id, role2text(rsc->role), role2text(rsc->next_role)); role = rsc->role; /* Potentiall optional steps on brining the resource down and back up to the same level */ while (role != RSC_ROLE_STOPPED) { next_role = rsc_state_matrix[role][RSC_ROLE_STOPPED]; pe_rsc_trace(rsc, "Down: Executing: %s->%s (%s)%s", role2text(role), role2text(next_role), rsc->id, need_stop?" required":""); if (rsc_action_matrix[role][next_role] (rsc, current, !need_stop, data_set) == FALSE) { break; } role = next_role; } while (rsc->role <= rsc->next_role && role != rsc->role) { next_role = rsc_state_matrix[role][rsc->role]; pe_rsc_trace(rsc, "Up: Executing: %s->%s (%s)%s", role2text(role), role2text(next_role), rsc->id, need_stop?" required":""); if (rsc_action_matrix[role][next_role] (rsc, chosen, !need_stop, data_set) == FALSE) { break; } role = next_role; } role = rsc->role; /* Required steps from this role to the next */ while (role != rsc->next_role) { next_role = rsc_state_matrix[role][rsc->next_role]; pe_rsc_trace(rsc, "Role: Executing: %s->%s = (%s)", role2text(role), role2text(rsc->next_role), role2text(next_role), rsc->id); if (rsc_action_matrix[role][next_role] (rsc, chosen, FALSE, data_set) == FALSE) { break; } role = next_role; } if (rsc->next_role != RSC_ROLE_STOPPED || is_set(rsc->flags, pe_rsc_managed) == FALSE) { start = start_action(rsc, chosen, TRUE); Recurring(rsc, start, chosen, data_set); Recurring_Stopped(rsc, start, chosen, data_set); } else { Recurring_Stopped(rsc, NULL, NULL, data_set); } } void native_internal_constraints(resource_t * rsc, pe_working_set_t * data_set) { /* This function is on the critical path and worth optimizing as much as possible */ resource_t *top = uber_parent(rsc); int type = pe_order_optional | pe_order_implies_then | pe_order_restart; custom_action_order(rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL, rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, type, data_set); if (top->variant == pe_master || rsc->role > RSC_ROLE_SLAVE) { custom_action_order(rsc, generate_op_key(rsc->id, RSC_DEMOTE, 0), NULL, rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL, pe_order_implies_first_master, data_set); custom_action_order(rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, rsc, generate_op_key(rsc->id, RSC_PROMOTE, 0), NULL, pe_order_runnable_left, data_set); } if (is_not_set(rsc->flags, pe_rsc_managed)) { pe_rsc_trace(rsc, "Skipping fencing constraints for unmanaged resource: %s", rsc->id); return; } { action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); custom_action_order(rsc, stop_key(rsc), NULL, NULL, strdup(all_stopped->task), all_stopped, pe_order_implies_then | pe_order_runnable_left, data_set); } if (g_hash_table_size(rsc->utilization) > 0 && safe_str_neq(data_set->placement_strategy, "default")) { GHashTableIter iter; node_t *next = NULL; GListPtr gIter = NULL; pe_rsc_trace(rsc, "Creating utilization constraints for %s - strategy: %s", rsc->id, data_set->placement_strategy); for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t *) gIter->data; char *load_stopped_task = crm_concat(LOAD_STOPPED, current->details->uname, '_'); action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set); if (load_stopped->node == NULL) { load_stopped->node = node_copy(current); update_action_flags(load_stopped, pe_action_optional | pe_action_clear); } custom_action_order(rsc, stop_key(rsc), NULL, NULL, load_stopped_task, load_stopped, pe_order_load, data_set); } g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&next)) { char *load_stopped_task = crm_concat(LOAD_STOPPED, next->details->uname, '_'); action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set); if (load_stopped->node == NULL) { load_stopped->node = node_copy(next); update_action_flags(load_stopped, pe_action_optional | pe_action_clear); } custom_action_order(NULL, strdup(load_stopped_task), load_stopped, rsc, start_key(rsc), NULL, pe_order_load, data_set); free(load_stopped_task); } } if (rsc->container) { custom_action_order(rsc->container, generate_op_key(rsc->container->id, RSC_START, 0), NULL, rsc, generate_op_key(rsc->id, RSC_START, 0), NULL, pe_order_implies_then | pe_order_runnable_left, data_set); custom_action_order(rsc, generate_op_key(rsc->id, RSC_STOP, 0), NULL, rsc->container, generate_op_key(rsc->container->id, RSC_STOP, 0), NULL, pe_order_implies_first, data_set); rsc_colocation_new("resource-with-containter", NULL, INFINITY, rsc, rsc->container, NULL, NULL, data_set); } } void native_rsc_colocation_lh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { if (rsc_lh == NULL) { pe_err("rsc_lh was NULL for %s", constraint->id); return; } else if (constraint->rsc_rh == NULL) { pe_err("rsc_rh was NULL for %s", constraint->id); return; } pe_rsc_trace(rsc_lh, "Processing colocation constraint between %s and %s", rsc_lh->id, rsc_rh->id); rsc_rh->cmds->rsc_colocation_rh(rsc_lh, rsc_rh, constraint); } enum filter_colocation_res { influence_nothing = 0, influence_rsc_location, influence_rsc_priority, }; static enum filter_colocation_res filter_colocation_constraint(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { if (constraint->score == 0) { return influence_nothing; } /* rh side must be allocated before we can process constraint */ if (is_set(rsc_rh->flags, pe_rsc_provisional)) { return influence_nothing; } if ((constraint->role_lh >= RSC_ROLE_SLAVE) && rsc_lh->parent && rsc_lh->parent->variant == pe_master && is_not_set(rsc_lh->flags, pe_rsc_provisional)) { /* LH and RH resources have already been allocated, place the correct * priority oh LH rsc for the given multistate resource role */ return influence_rsc_priority; } if (is_not_set(rsc_lh->flags, pe_rsc_provisional)) { /* error check */ struct node_shared_s *details_lh; struct node_shared_s *details_rh; if ((constraint->score > -INFINITY) && (constraint->score < INFINITY)) { return influence_nothing; } details_rh = rsc_rh->allocated_to ? rsc_rh->allocated_to->details : NULL; details_lh = rsc_lh->allocated_to ? rsc_lh->allocated_to->details : NULL; if (constraint->score == INFINITY && details_lh != details_rh) { crm_err("%s and %s are both allocated" " but to different nodes: %s vs. %s", rsc_lh->id, rsc_rh->id, details_lh ? details_lh->uname : "n/a", details_rh ? details_rh->uname : "n/a"); } else if (constraint->score == -INFINITY && details_lh == details_rh) { crm_err("%s and %s are both allocated" " but to the SAME node: %s", rsc_lh->id, rsc_rh->id, details_rh ? details_rh->uname : "n/a"); } return influence_nothing; } if (constraint->score > 0 && constraint->role_lh != RSC_ROLE_UNKNOWN && constraint->role_lh != rsc_lh->next_role) { crm_trace( "LH: Skipping constraint: \"%s\" state filter nextrole is %s", role2text(constraint->role_lh), role2text(rsc_lh->next_role)); return influence_nothing; } if (constraint->score > 0 && constraint->role_rh != RSC_ROLE_UNKNOWN && constraint->role_rh != rsc_rh->next_role) { crm_trace( "RH: Skipping constraint: \"%s\" state filter", role2text(constraint->role_rh)); return FALSE; } if (constraint->score < 0 && constraint->role_lh != RSC_ROLE_UNKNOWN && constraint->role_lh == rsc_lh->next_role) { crm_trace( "LH: Skipping -ve constraint: \"%s\" state filter", role2text(constraint->role_lh)); return influence_nothing; } if (constraint->score < 0 && constraint->role_rh != RSC_ROLE_UNKNOWN && constraint->role_rh == rsc_rh->next_role) { crm_trace( "RH: Skipping -ve constraint: \"%s\" state filter", role2text(constraint->role_rh)); return influence_nothing; } return influence_rsc_location; } static void influence_priority(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { const char *rh_value = NULL; const char *lh_value = NULL; const char *attribute = "#id"; int score_multiplier = 1; if (constraint->node_attribute != NULL) { attribute = constraint->node_attribute; } if (!rsc_rh->allocated_to || !rsc_lh->allocated_to) { return; } lh_value = g_hash_table_lookup(rsc_lh->allocated_to->details->attrs, attribute); rh_value = g_hash_table_lookup(rsc_rh->allocated_to->details->attrs, attribute); if (!safe_str_eq(lh_value, rh_value)) { return; } if (constraint->role_rh && (constraint->role_rh != rsc_rh->next_role)) { return; } if (constraint->role_lh == RSC_ROLE_SLAVE) { score_multiplier = -1; } rsc_lh->priority = merge_weights(score_multiplier * constraint->score, rsc_lh->priority); } static void colocation_match(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { const char *tmp = NULL; const char *value = NULL; const char *attribute = "#id"; GHashTable *work = NULL; gboolean do_check = FALSE; GHashTableIter iter; node_t *node = NULL; if (constraint->node_attribute != NULL) { attribute = constraint->node_attribute; } if (rsc_rh->allocated_to) { value = g_hash_table_lookup(rsc_rh->allocated_to->details->attrs, attribute); do_check = TRUE; } else if (constraint->score < 0) { /* nothing to do: * anti-colocation with something thats not running */ return; } work = node_hash_dup(rsc_lh->allowed_nodes); g_hash_table_iter_init(&iter, work); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { tmp = g_hash_table_lookup(node->details->attrs, attribute); if (do_check && safe_str_eq(tmp, value)) { if (constraint->score < INFINITY) { pe_rsc_trace(rsc_lh, "%s: %s.%s += %d", constraint->id, rsc_lh->id, node->details->uname, constraint->score); node->weight = merge_weights(constraint->score, node->weight); } } else if (do_check == FALSE || constraint->score >= INFINITY) { pe_rsc_trace(rsc_lh, "%s: %s.%s -= %d (%s)", constraint->id, rsc_lh->id, node->details->uname, constraint->score, do_check ? "failed" : "unallocated"); node->weight = merge_weights(-constraint->score, node->weight); } } if (can_run_any(work) || constraint->score <= -INFINITY || constraint->score >= INFINITY) { g_hash_table_destroy(rsc_lh->allowed_nodes); rsc_lh->allowed_nodes = work; work = NULL; } else { char *score = score2char(constraint->score); pe_rsc_info(rsc_lh, "%s: Rolling back scores from %s (%d, %s)", rsc_lh->id, rsc_rh->id, do_check, score); free(score); } if (work) { g_hash_table_destroy(work); } } void native_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint) { enum filter_colocation_res filter_results; filter_results = filter_colocation_constraint(rsc_lh, rsc_rh, constraint); switch (filter_results) { case influence_rsc_priority: influence_priority(rsc_lh, rsc_rh, constraint); break; case influence_rsc_location: pe_rsc_trace(rsc_lh, "%sColocating %s with %s (%s, weight=%d)", constraint->score >= 0 ? "" : "Anti-", rsc_lh->id, rsc_rh->id, constraint->id, constraint->score); colocation_match(rsc_lh, rsc_rh, constraint); break; case influence_nothing: default: return; } } static gboolean filter_rsc_ticket(resource_t * rsc_lh, rsc_ticket_t * rsc_ticket) { if (rsc_ticket->role_lh != RSC_ROLE_UNKNOWN && rsc_ticket->role_lh != rsc_lh->role) { pe_rsc_trace(rsc_lh, "LH: Skipping constraint: \"%s\" state filter", role2text(rsc_ticket->role_lh)); return FALSE; } return TRUE; } void rsc_ticket_constraint(resource_t * rsc_lh, rsc_ticket_t * rsc_ticket, pe_working_set_t * data_set) { if (rsc_ticket == NULL) { pe_err("rsc_ticket was NULL"); return; } if (rsc_lh == NULL) { pe_err("rsc_lh was NULL for %s", rsc_ticket->id); return; } if (rsc_ticket->ticket->granted && rsc_ticket->ticket->standby == FALSE) { return; } if (rsc_lh->children) { GListPtr gIter = rsc_lh->children; pe_rsc_trace(rsc_lh, "Processing ticket dependencies from %s", rsc_lh->id); for (; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; rsc_ticket_constraint(child_rsc, rsc_ticket, data_set); } return; } pe_rsc_trace(rsc_lh, "%s: Processing ticket dependency on %s (%s, %s)", rsc_lh->id, rsc_ticket->ticket->id, rsc_ticket->id, role2text(rsc_ticket->role_lh)); if (rsc_ticket->ticket->granted == FALSE && g_list_length(rsc_lh->running_on) > 0) { GListPtr gIter = NULL; switch (rsc_ticket->loss_policy) { case loss_ticket_stop: resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__", data_set); break; case loss_ticket_demote: /*Promotion score will be set to -INFINITY in master_promotion_order() */ if (rsc_ticket->role_lh != RSC_ROLE_MASTER) { resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__", data_set); } break; case loss_ticket_fence: if (filter_rsc_ticket(rsc_lh, rsc_ticket) == FALSE) { return; } resource_location(rsc_lh, NULL, -INFINITY, "__loss_of_ticket__", data_set); for (gIter = rsc_lh->running_on; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; crm_warn("Node %s will be fenced for deadman", node->details->uname); node->details->unclean = TRUE; } break; case loss_ticket_freeze: if (filter_rsc_ticket(rsc_lh, rsc_ticket) == FALSE) { return; } if (g_list_length(rsc_lh->running_on) > 0) { clear_bit(rsc_lh->flags, pe_rsc_managed); set_bit(rsc_lh->flags, pe_rsc_block); } break; } } else if (rsc_ticket->ticket->granted == FALSE){ if (rsc_ticket->role_lh != RSC_ROLE_MASTER || rsc_ticket->loss_policy == loss_ticket_stop) { resource_location(rsc_lh, NULL, -INFINITY, "__no_ticket__", data_set); } } else if (rsc_ticket->ticket->standby) { if (rsc_ticket->role_lh != RSC_ROLE_MASTER || rsc_ticket->loss_policy == loss_ticket_stop) { resource_location(rsc_lh, NULL, -INFINITY, "__ticket_standby__", data_set); } } } enum pe_action_flags native_action_flags(action_t * action, node_t * node) { return action->flags; } enum pe_graph_flags native_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags, enum pe_action_flags filter, enum pe_ordering type) { /* flags == get_action_flags(first, then_node) called from update_action() */ enum pe_graph_flags changed = pe_graph_none; enum pe_action_flags then_flags = then->flags; enum pe_action_flags first_flags = first->flags; if (type & pe_order_asymmetrical) { resource_t *then_rsc = then->rsc; enum rsc_role_e then_rsc_role = then_rsc ? then_rsc->fns->state(then_rsc, TRUE) : 0; if (!then_rsc) { /* ignore */ } else if ((then_rsc_role == RSC_ROLE_STOPPED) && safe_str_eq(then->task, RSC_STOP)) { /* ignore... if 'then' is supposed to be stopped after 'first', but * then is already stopped, there is nothing to be done when non-symmetrical. */ } else if ((then_rsc_role == RSC_ROLE_STARTED) && safe_str_eq(then->task, RSC_START)) { /* ignore... if 'then' is supposed to be started after 'first', but * then is already started, there is nothing to be done when non-symmetrical. */ } else if (!(first->flags & pe_action_runnable)) { /* prevent 'then' action from happening if 'first' is not runnable and * 'then' has not yet occurred. */ pe_clear_action_bit(then, pe_action_runnable); pe_clear_action_bit(then, pe_action_optional); pe_rsc_trace(then->rsc, "Unset optional and runnable on %s", then->uuid); } else { /* ignore... then is allowed to start/stop if it wants to. */ } } if (type & pe_order_implies_first) { if ((filter & pe_action_optional) && (flags & pe_action_optional) == 0) { pe_rsc_trace(first->rsc, "Unset optional on %s because of %s", first->uuid, then->uuid); pe_clear_action_bit(first, pe_action_optional); } } if (type & pe_order_implies_first_master) { if ((filter & pe_action_optional) && ((then->flags & pe_action_optional) == FALSE) && then->rsc && (then->rsc->role == RSC_ROLE_MASTER)) { clear_bit(first->flags, pe_action_optional); } } if (is_set(type, pe_order_runnable_left) && is_set(filter, pe_action_runnable) && is_set(then->flags, pe_action_runnable) && is_set(flags, pe_action_runnable) == FALSE) { pe_rsc_trace(then->rsc, "Unset runnable on %s because of %s", then->uuid, first->uuid); pe_clear_action_bit(then, pe_action_runnable); } if (is_set(type, pe_order_implies_then) && is_set(filter, pe_action_optional) && is_set(then->flags, pe_action_optional) && is_set(flags, pe_action_optional) == FALSE) { pe_rsc_trace(then->rsc, "Unset optional on %s because of %s", then->uuid, first->uuid); pe_clear_action_bit(then, pe_action_optional); } if (is_set(type, pe_order_restart)) { const char *reason = NULL; CRM_ASSERT(first->rsc && first->rsc->variant == pe_native); CRM_ASSERT(then->rsc && then->rsc->variant == pe_native); if ((filter & pe_action_runnable) && (then->flags & pe_action_runnable) == 0) { reason = "shutdown"; } if ((filter & pe_action_optional) && (then->flags & pe_action_optional) == 0) { reason = "recover"; } if (reason && is_set(first->flags, pe_action_optional) && is_set(first->flags, pe_action_runnable)) { pe_rsc_trace(first->rsc, "Handling %s: %s -> %s", reason, first->uuid, then->uuid); pe_clear_action_bit(first, pe_action_optional); } if (reason && is_not_set(first->flags, pe_action_optional) && is_not_set(first->flags, pe_action_runnable)) { pe_rsc_trace(then->rsc, "Handling %s: %s -> %s", reason, first->uuid, then->uuid); pe_clear_action_bit(then, pe_action_runnable); } } if (then_flags != then->flags) { changed |= pe_graph_updated_then; pe_rsc_trace(then->rsc, "Then: Flags for %s on %s are now 0x%.6x (was 0x%.6x) because of %s 0x%.6x", then->uuid, then->node ? then->node->details->uname : "[none]", then->flags, then_flags, first->uuid, first->flags); } if (first_flags != first->flags) { changed |= pe_graph_updated_first; pe_rsc_trace(first->rsc, "First: Flags for %s on %s are now 0x%.6x (was 0x%.6x) because of %s 0x%.6x", first->uuid, first->node ? first->node->details->uname : "[none]", first->flags, first_flags, then->uuid, then->flags); } return changed; } void native_rsc_location(resource_t * rsc, rsc_to_node_t * constraint) { GListPtr gIter = NULL; GHashTableIter iter; node_t *node = NULL; if (constraint == NULL) { pe_err("Constraint is NULL"); return; } else if (rsc == NULL) { pe_err("LHS of rsc_to_node (%s) is NULL", constraint->id); return; } pe_rsc_trace(rsc, "Applying %s (%s) to %s", constraint->id, role2text(constraint->role_filter), rsc->id); /* take "lifetime" into account */ if (constraint->role_filter > 0 && constraint->role_filter != rsc->next_role) { pe_rsc_debug(rsc, "Constraint (%s) is not active (role : %s)", constraint->id, role2text(constraint->role_filter)); return; } else if (is_active(constraint) == FALSE) { pe_rsc_trace(rsc, "Constraint (%s) is not active", constraint->id); return; } if (constraint->node_list_rh == NULL) { pe_rsc_trace(rsc, "RHS of constraint %s is NULL", constraint->id); return; } for (gIter = constraint->node_list_rh; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; node_t *other_node = NULL; other_node = (node_t *) pe_hash_table_lookup(rsc->allowed_nodes, node->details->id); if (other_node != NULL) { pe_rsc_trace(rsc, "%s + %s: %d + %d", node->details->uname, other_node->details->uname, node->weight, other_node->weight); other_node->weight = merge_weights(other_node->weight, node->weight); } else { node_t *new_node = node_copy(node); g_hash_table_insert(rsc->allowed_nodes, (gpointer) new_node->details->id, new_node); } } g_hash_table_iter_init(&iter, rsc->allowed_nodes); while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { pe_rsc_trace(rsc, "%s + %s : %d", rsc->id, node->details->uname, node->weight); } } void native_expand(resource_t * rsc, pe_working_set_t * data_set) { GListPtr gIter = NULL; pe_rsc_trace(rsc, "Processing actions from %s", rsc->id); for (gIter = rsc->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; crm_trace("processing action %d for rsc=%s", action->id, rsc->id); graph_element_from_action(action, data_set); } for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; child_rsc->cmds->expand(child_rsc, data_set); } } void #define log_change(fmt, args...) do { \ if(terminal) { \ printf(" * "fmt"\n", ##args); \ } else { \ crm_notice(fmt, ##args); \ } \ } while(0) LogActions(resource_t * rsc, pe_working_set_t * data_set, gboolean terminal) { node_t *next = NULL; node_t *current = NULL; action_t *stop = NULL; action_t *start = NULL; action_t *demote = NULL; action_t *promote = NULL; char *key = NULL; gboolean moving = FALSE; GListPtr possible_matches = NULL; if (rsc->children) { GListPtr gIter = NULL; for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; LogActions(child_rsc, data_set, terminal); } return; } next = rsc->allocated_to; if (rsc->running_on) { if (g_list_length(rsc->running_on) > 1 && rsc->partial_migration_source) { current = rsc->partial_migration_source; } else { current = rsc->running_on->data; } if (rsc->role == RSC_ROLE_STOPPED) { /* * This can occur when resources are being recovered * We fiddle with the current role in native_create_actions() */ rsc->role = RSC_ROLE_STARTED; } } if (current == NULL && is_set(rsc->flags, pe_rsc_orphan)) { /* Don't log stopped orphans */ return; } if (is_not_set(rsc->flags, pe_rsc_managed) || (current == NULL && next == NULL)) { pe_rsc_info(rsc, "Leave %s\t(%s%s)", rsc->id, role2text(rsc->role), is_not_set(rsc->flags, pe_rsc_managed) ? " unmanaged" : ""); return; } if (current != NULL && next != NULL && safe_str_neq(current->details->id, next->details->id)) { moving = TRUE; } key = start_key(rsc); possible_matches = find_actions(rsc->actions, key, next); free(key); if (possible_matches) { start = possible_matches->data; g_list_free(possible_matches); } key = stop_key(rsc); possible_matches = find_actions(rsc->actions, key, next); free(key); if (possible_matches) { stop = possible_matches->data; g_list_free(possible_matches); } key = promote_key(rsc); possible_matches = find_actions(rsc->actions, key, next); free(key); if (possible_matches) { promote = possible_matches->data; g_list_free(possible_matches); } key = demote_key(rsc); possible_matches = find_actions(rsc->actions, key, next); free(key); if (possible_matches) { demote = possible_matches->data; g_list_free(possible_matches); } if (rsc->role == rsc->next_role) { key = generate_op_key(rsc->id, RSC_MIGRATED, 0); possible_matches = find_actions(rsc->actions, key, next); free(key); CRM_CHECK(next != NULL,); if (next == NULL) { } else if (possible_matches && current) { log_change("Migrate %s\t(%s %s -> %s)", rsc->id, role2text(rsc->role), current->details->uname, next->details->uname); g_list_free(possible_matches); } else if(is_set(rsc->flags, pe_rsc_reload)) { log_change("Reload %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if (start == NULL || is_set(start->flags, pe_action_optional)) { pe_rsc_info(rsc, "Leave %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if (start && is_set(start->flags, pe_action_runnable) == FALSE) { log_change("Stop %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if (moving && current) { log_change("%s %s\t(%s %s -> %s)", is_set(rsc->flags, pe_rsc_failed)?"Recover":"Move ", rsc->id, role2text(rsc->role), current->details->uname, next->details->uname); } else if (is_set(rsc->flags, pe_rsc_failed)) { log_change("Recover %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else { log_change("Restart %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } return; } if (rsc->role > RSC_ROLE_SLAVE && rsc->role > rsc->next_role) { CRM_CHECK(current != NULL,); if (current != NULL) { gboolean allowed = FALSE; if (demote != NULL && (demote->flags & pe_action_runnable)) { allowed = TRUE; } log_change("Demote %s\t(%s -> %s %s%s)", rsc->id, role2text(rsc->role), role2text(rsc->next_role), current->details->uname, allowed ? "" : " - blocked"); if (stop != NULL && is_not_set(stop->flags, pe_action_optional) && rsc->next_role > RSC_ROLE_STOPPED) { if (is_set(rsc->flags, pe_rsc_failed)) { log_change("Recover %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if(is_set(rsc->flags, pe_rsc_reload)) { log_change("Reload %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else { log_change("Restart %s\t(%s %s)", rsc->id, role2text(rsc->next_role), next->details->uname); } } } } else if (rsc->next_role == RSC_ROLE_STOPPED) { GListPtr gIter = NULL; CRM_CHECK(current != NULL,); for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *node = (node_t *) gIter->data; log_change("Stop %s\t(%s)", rsc->id, node->details->uname); } } if (moving) { log_change("Move %s\t(%s %s -> %s)", rsc->id, role2text(rsc->next_role), current->details->uname, next->details->uname); } if (rsc->role == RSC_ROLE_STOPPED) { gboolean allowed = FALSE; if(start && (start->flags & pe_action_runnable)) { allowed = TRUE; } CRM_CHECK(next != NULL,); if (next != NULL) { log_change("Start %s\t(%s%s)", rsc->id, next->details->uname, allowed?"":" - blocked"); } if(allowed == FALSE) { return; } } if (rsc->next_role > RSC_ROLE_SLAVE && rsc->role < rsc->next_role) { gboolean allowed = FALSE; CRM_CHECK(next != NULL,); if (stop != NULL && is_not_set(stop->flags, pe_action_optional) && rsc->role > RSC_ROLE_STOPPED) { if (is_set(rsc->flags, pe_rsc_failed)) { log_change("Recover %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else if(is_set(rsc->flags, pe_rsc_reload)) { log_change("Reload %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } else { log_change("Restart %s\t(%s %s)", rsc->id, role2text(rsc->role), next->details->uname); } } if (promote && (promote->flags & pe_action_runnable)) { allowed = TRUE; } log_change("Promote %s\t(%s -> %s %s%s)", rsc->id, role2text(rsc->role), role2text(rsc->next_role), next->details->uname, allowed ? "" : " - blocked"); } } gboolean StopRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set) { GListPtr gIter = NULL; pe_rsc_trace(rsc, "%s", rsc->id); for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t *) gIter->data; action_t *stop; if (rsc->partial_migration_target) { if(rsc->partial_migration_target->details == current->details) { pe_rsc_trace(rsc, "Filtered %s -> %s %s", current->details->uname, next->details->uname, rsc->id); continue; } else { pe_rsc_trace(rsc, "Forced on %s %s", current->details->uname, rsc->id); optional = FALSE; } } pe_rsc_trace(rsc, "%s on %s", rsc->id, current->details->uname); stop = stop_action(rsc, current, optional); if(is_not_set(rsc->flags, pe_rsc_managed)) { update_action_flags(stop, pe_action_runnable|pe_action_clear); } if (is_set(data_set->flags, pe_flag_remove_after_stop)) { DeleteRsc(rsc, current, optional, data_set); } } return TRUE; } gboolean StartRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set) { action_t *start = NULL; pe_rsc_trace(rsc, "%s on %s %d", rsc->id, next?next->details->uname:"N/A", optional); start = start_action(rsc, next, TRUE); if (is_set(start->flags, pe_action_runnable) && optional == FALSE) { update_action_flags(start, pe_action_optional | pe_action_clear); } return TRUE; } gboolean PromoteRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set) { char *key = NULL; GListPtr gIter = NULL; gboolean runnable = TRUE; GListPtr action_list = NULL; pe_rsc_trace(rsc, "%s on %s", rsc->id, next?next->details->uname:"N/A"); CRM_CHECK(next != NULL, return FALSE); key = start_key(rsc); action_list = find_actions_exact(rsc->actions, key, next); free(key); for (gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *start = (action_t *) gIter->data; if (is_set(start->flags, pe_action_runnable) == FALSE) { runnable = FALSE; } } g_list_free(action_list); if (runnable) { promote_action(rsc, next, optional); return TRUE; } pe_rsc_debug(rsc, "%s\tPromote %s (canceled)", next->details->uname, rsc->id); key = promote_key(rsc); action_list = find_actions_exact(rsc->actions, key, next); free(key); for (gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *promote = (action_t *) gIter->data; update_action_flags(promote, pe_action_runnable | pe_action_clear); } g_list_free(action_list); return TRUE; } gboolean DemoteRsc(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set) { GListPtr gIter = NULL; pe_rsc_trace(rsc, "%s", rsc->id); /* CRM_CHECK(rsc->next_role == RSC_ROLE_SLAVE, return FALSE); */ for (gIter = rsc->running_on; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t *) gIter->data; pe_rsc_trace(rsc, "%s on %s", rsc->id, next?next->details->uname:"N/A"); demote_action(rsc, current, optional); } return TRUE; } gboolean RoleError(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set) { crm_err("%s on %s", rsc->id, next?next->details->uname:"N/A"); CRM_CHECK(FALSE, return FALSE); return FALSE; } gboolean NullOp(resource_t * rsc, node_t * next, gboolean optional, pe_working_set_t * data_set) { pe_rsc_trace(rsc, "%s", rsc->id); return FALSE; } gboolean DeleteRsc(resource_t * rsc, node_t * node, gboolean optional, pe_working_set_t * data_set) { - action_t *delete = NULL; - #if DELETE_THEN_REFRESH + action_t *delete = NULL; action_t *refresh = NULL; #endif if (is_set(rsc->flags, pe_rsc_failed)) { pe_rsc_trace(rsc, "Resource %s not deleted from %s: failed", rsc->id, node->details->uname); return FALSE; } else if (node == NULL) { pe_rsc_trace(rsc, "Resource %s not deleted: NULL node", rsc->id); return FALSE; } else if (node->details->unclean || node->details->online == FALSE) { pe_rsc_trace(rsc, "Resource %s not deleted from %s: unrunnable", rsc->id, node->details->uname); return FALSE; } crm_notice("Removing %s from %s", rsc->id, node->details->uname); +#if DELETE_THEN_REFRESH delete = delete_action(rsc, node, optional); +#else + delete_action(rsc, node, optional); +#endif new_rsc_order(rsc, RSC_STOP, rsc, RSC_DELETE, optional ? pe_order_implies_then : pe_order_optional, data_set); new_rsc_order(rsc, RSC_DELETE, rsc, RSC_START, optional ? pe_order_implies_then : pe_order_optional, data_set); #if DELETE_THEN_REFRESH refresh = custom_action(NULL, strdup(CRM_OP_LRM_REFRESH), CRM_OP_LRM_REFRESH, node, FALSE, TRUE, data_set); add_hash_param(refresh->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); order_actions(delete, refresh, pe_order_optional); #endif return TRUE; } #include <../lib/pengine/unpack.h> #define set_char(x) last_rsc_id[lpc] = x; complete = TRUE; static char * increment_clone(char *last_rsc_id) { int lpc = 0; int len = 0; char *tmp = NULL; gboolean complete = FALSE; CRM_CHECK(last_rsc_id != NULL, return NULL); if (last_rsc_id != NULL) { len = strlen(last_rsc_id); } lpc = len - 1; while (complete == FALSE && lpc > 0) { switch (last_rsc_id[lpc]) { case 0: lpc--; break; case '0': set_char('1'); break; case '1': set_char('2'); break; case '2': set_char('3'); break; case '3': set_char('4'); break; case '4': set_char('5'); break; case '5': set_char('6'); break; case '6': set_char('7'); break; case '7': set_char('8'); break; case '8': set_char('9'); break; case '9': last_rsc_id[lpc] = '0'; lpc--; break; case ':': tmp = last_rsc_id; last_rsc_id = calloc(1, len + 2); memcpy(last_rsc_id, tmp, len); last_rsc_id[++lpc] = '1'; last_rsc_id[len] = '0'; last_rsc_id[len + 1] = 0; complete = TRUE; free(tmp); break; default: crm_err("Unexpected char: %c (%d)", last_rsc_id[lpc], lpc); return NULL; break; } } return last_rsc_id; } static node_t * probe_grouped_clone(resource_t * rsc, node_t * node, pe_working_set_t * data_set) { node_t *running = NULL; resource_t *top = uber_parent(rsc); if (running == NULL && is_set(top->flags, pe_rsc_unique) == FALSE) { /* Annoyingly we also need to check any other clone instances * Clumsy, but it will work. * * An alternative would be to update known_on for every peer * during process_rsc_state() * * This code desperately needs optimization * ptest -x with 100 nodes, 100 clones and clone-max=10: * No probes O(25s) * Detection without clone loop O(3m) * Detection with clone loop O(8m) ptest[32211]: 2010/02/18_14:27:55 CRIT: stage5: Probing for unknown resources ptest[32211]: 2010/02/18_14:33:39 CRIT: stage5: Done ptest[32211]: 2010/02/18_14:35:05 CRIT: stage7: Updating action states ptest[32211]: 2010/02/18_14:35:05 CRIT: stage7: Done */ char *clone_id = clone_zero(rsc->id); resource_t *peer = pe_find_resource(top->children, clone_id); while (peer && running == NULL) { running = pe_hash_table_lookup(peer->known_on, node->details->id); if (running != NULL) { /* we already know the status of the resource on this node */ pe_rsc_trace(rsc, "Skipping active clone: %s", rsc->id); free(clone_id); return running; } clone_id = increment_clone(clone_id); peer = pe_find_resource(data_set->resources, clone_id); } free(clone_id); } return running; } gboolean native_create_probe(resource_t * rsc, node_t * node, action_t * complete, gboolean force, pe_working_set_t * data_set) { char *key = NULL; action_t *probe = NULL; node_t *running = NULL; resource_t *top = uber_parent(rsc); static const char *rc_master = NULL; static const char *rc_inactive = NULL; if (rc_inactive == NULL) { rc_inactive = crm_itoa(PCMK_EXECRA_NOT_RUNNING); rc_master = crm_itoa(PCMK_EXECRA_RUNNING_MASTER); } CRM_CHECK(node != NULL, return FALSE); if (force == FALSE && is_not_set(data_set->flags, pe_flag_startup_probes)) { pe_rsc_trace(rsc, "Skipping active resource detection for %s", rsc->id); return FALSE; } if (rsc->children) { GListPtr gIter = NULL; gboolean any_created = FALSE; for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; any_created = child_rsc->cmds->create_probe(child_rsc, node, complete, force, data_set) || any_created; } return any_created; } else if (rsc->container) { pe_rsc_trace(rsc, "Skipping %s: it is within container %s", rsc->id, rsc->container->id); return FALSE; } if (is_set(rsc->flags, pe_rsc_orphan)) { pe_rsc_trace(rsc, "Skipping orphan: %s", rsc->id); return FALSE; } running = g_hash_table_lookup(rsc->known_on, node->details->id); if (running == NULL && is_set(rsc->flags, pe_rsc_unique) == FALSE) { /* Anonymous clones */ if (rsc->parent == top) { running = g_hash_table_lookup(rsc->parent->known_on, node->details->id); } else { /* Grouped anonymous clones need extra special handling */ running = probe_grouped_clone(rsc, node, data_set); } } if (force == FALSE && running != NULL) { /* we already know the status of the resource on this node */ pe_rsc_trace(rsc, "Skipping active: %s on %s", rsc->id, node->details->uname); return FALSE; } key = generate_op_key(rsc->id, RSC_STATUS, 0); probe = custom_action(rsc, key, RSC_STATUS, node, FALSE, TRUE, data_set); update_action_flags(probe, pe_action_optional | pe_action_clear); /* Check if the node needs to be unfenced first */ if(is_set(rsc->flags, pe_rsc_needs_unfencing)) { action_t *unfence = pe_fence_op(node, "on", data_set); crm_notice("Unfencing %s for %s", node->details->uname, rsc->id); order_actions(unfence, probe, pe_order_implies_then); /* The lack of ordering constraints on STONITH_UP would * traditionally mean unfencing is initiated /before/ the * devices are started. * * However this is a non-issue as stonithd is now smart * enough to be able to use devices directly from the cib */ } /* * We need to know if it's running_on (not just known_on) this node * to correctly determine the target rc. */ running = pe_find_node_id(rsc->running_on, node->details->id); if (running == NULL) { add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, rc_inactive); } else if (rsc->role == RSC_ROLE_MASTER) { add_hash_param(probe->meta, XML_ATTR_TE_TARGET_RC, rc_master); } pe_rsc_debug(rsc, "Probing %s on %s (%s)", rsc->id, node->details->uname, role2text(rsc->role)); order_actions(probe, complete, pe_order_implies_then); return TRUE; } static void native_start_constraints(resource_t * rsc, action_t * stonith_op, gboolean is_stonith, pe_working_set_t * data_set) { node_t *target = stonith_op ? stonith_op->node : NULL; GListPtr gIter = NULL; action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set); action_t *stonith_done = get_pseudo_op(STONITH_DONE, data_set); for (gIter = rsc->actions; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (action->needs == rsc_req_stonith) { order_actions(stonith_done, action, pe_order_optional); } else if (target != NULL && safe_str_eq(action->task, RSC_START) && NULL == pe_hash_table_lookup(rsc->known_on, target->details->id)) { /* if known == NULL, then we dont know if * the resource is active on the node * we're about to shoot * * in this case, regardless of action->needs, * the only safe option is to wait until * the node is shot before doing anything * to with the resource * * its analogous to waiting for all the probes * for rscX to complete before starting rscX * * the most likely explaination is that the * DC died and took its status with it */ pe_rsc_debug(rsc, "Ordering %s after %s recovery", action->uuid, target->details->uname); order_actions(all_stopped, action, pe_order_optional | pe_order_runnable_left); } } } static void native_stop_constraints(resource_t * rsc, action_t * stonith_op, gboolean is_stonith, pe_working_set_t * data_set) { char *key = NULL; GListPtr gIter = NULL; GListPtr action_list = NULL; resource_t *top = uber_parent(rsc); key = stop_key(rsc); action_list = find_actions(rsc->actions, key, stonith_op->node); free(key); /* add the stonith OP as a stop pre-req and the mark the stop * as a pseudo op - since its now redundant */ for (gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (action->node->details->online && action->node->details->unclean == FALSE && is_set(rsc->flags, pe_rsc_failed)) { continue; } if (is_set(rsc->flags, pe_rsc_failed)) { crm_notice("Stop of failed resource %s is" " implicit after %s is fenced", rsc->id, action->node->details->uname); } else { crm_info("%s is implicit after %s is fenced", action->uuid, action->node->details->uname); } /* the stop would never complete and is * now implied by the stonith operation */ update_action_flags(action, pe_action_pseudo); update_action_flags(action, pe_action_runnable); update_action_flags(action, pe_action_implied_by_stonith); { action_t *parent_stop = find_first_action(top->actions, NULL, RSC_STOP, NULL); order_actions(stonith_op, action, pe_order_optional); order_actions(stonith_op, parent_stop, pe_order_optional); } if (is_set(rsc->flags, pe_rsc_notify)) { /* Create a second notification that will be delivered * immediately after the node is fenced * * Basic problem: * - C is a clone active on the node to be shot and stopping on another * - R is a resource that depends on C * * + C.stop depends on R.stop * + C.stopped depends on STONITH * + C.notify depends on C.stopped * + C.healthy depends on C.notify * + R.stop depends on C.healthy * * The extra notification here changes * + C.healthy depends on C.notify * into: * + C.healthy depends on C.notify' * + C.notify' depends on STONITH' * thus breaking the loop */ notify_data_t *n_data = create_notification_boundaries(rsc, RSC_STOP, NULL, stonith_op, data_set); crm_info("Creating secondary notification for %s", action->uuid); collect_notification_data(rsc, TRUE, FALSE, n_data); g_hash_table_insert(n_data->keys, strdup("notify_stop_resource"), strdup(rsc->id)); g_hash_table_insert(n_data->keys, strdup("notify_stop_uname"), strdup(action->node->details->uname)); create_notifications(uber_parent(rsc), n_data, data_set); free_notification_data(n_data); } /* From Bug #1601, successful fencing must be an input to a failed resources stop action. However given group(rA, rB) running on nodeX and B.stop has failed, A := stop healthy resource (rA.stop) B := stop failed resource (pseudo operation B.stop) C := stonith nodeX A requires B, B requires C, C requires A This loop would prevent the cluster from making progress. This block creates the "C requires A" dependency and therefore must (at least for now) be disabled. Instead, run the block above and treat all resources on nodeX as B would be (marked as a pseudo op depending on the STONITH). TODO: Break the "A requires B" dependency in update_action() and re-enable this block } else if(is_stonith == FALSE) { crm_info("Moving healthy resource %s" " off %s before fencing", rsc->id, node->details->uname); * stop healthy resources before the * stonith op * custom_action_order( rsc, stop_key(rsc), NULL, NULL,strdup(CRM_OP_FENCE),stonith_op, pe_order_optional, data_set); */ } g_list_free(action_list); key = demote_key(rsc); action_list = find_actions(rsc->actions, key, stonith_op->node); free(key); for (gIter = action_list; gIter != NULL; gIter = gIter->next) { action_t *action = (action_t *) gIter->data; if (action->node->details->online == FALSE || action->node->details->unclean == TRUE || is_set(rsc->flags, pe_rsc_failed)) { if (is_set(rsc->flags, pe_rsc_failed)) { pe_rsc_info(rsc, "Demote of failed resource %s is" " implict after %s is fenced", rsc->id, action->node->details->uname); } else { pe_rsc_info(rsc, "%s is implicit after %s is fenced", action->uuid, action->node->details->uname); } /* the stop would never complete and is * now implied by the stonith operation */ crm_trace("here - 1"); update_action_flags(action, pe_action_pseudo); update_action_flags(action, pe_action_runnable); if (is_stonith == FALSE) { order_actions(stonith_op, action, pe_order_optional); } } } g_list_free(action_list); } void rsc_stonith_ordering(resource_t * rsc, action_t * stonith_op, pe_working_set_t * data_set) { gboolean is_stonith = FALSE; if (rsc->children) { GListPtr gIter = NULL; for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; rsc_stonith_ordering(child_rsc, stonith_op, data_set); } return; } if (is_not_set(rsc->flags, pe_rsc_managed)) { pe_rsc_trace(rsc, "Skipping fencing constraints for unmanaged resource: %s", rsc->id); return; } /* Start constraints */ native_start_constraints(rsc, stonith_op, is_stonith, data_set); /* Stop constraints */ if(stonith_op) { native_stop_constraints(rsc, stonith_op, is_stonith, data_set); } } enum stack_activity { stack_stable = 0, stack_starting = 1, stack_stopping = 2, stack_middle = 4, }; static enum stack_activity find_clone_activity_on(resource_t * rsc, resource_t * target, node_t * node, const char *type) { int mode = stack_stable; action_t *active = NULL; if (target->children) { GListPtr gIter = NULL; for (gIter = target->children; gIter != NULL; gIter = gIter->next) { resource_t *child = (resource_t *) gIter->data; mode |= find_clone_activity_on(rsc, child, node, type); } return mode; } active = find_first_action(target->actions, NULL, RSC_START, NULL); if (active && is_set(active->flags, pe_action_optional) == FALSE && is_set(active->flags, pe_action_pseudo) == FALSE) { pe_rsc_debug(rsc, "%s: found scheduled %s action (%s)", rsc->id, active->uuid, type); mode |= stack_starting; } active = find_first_action(target->actions, NULL, RSC_STOP, node); if (active && is_set(active->flags, pe_action_optional) == FALSE && is_set(active->flags, pe_action_pseudo) == FALSE) { pe_rsc_debug(rsc, "%s: found scheduled %s action (%s)", rsc->id, active->uuid, type); mode |= stack_stopping; } return mode; } static enum stack_activity check_stack_element(resource_t * rsc, resource_t * other_rsc, const char *type) { resource_t *other_p = uber_parent(other_rsc); if (other_rsc == NULL || other_rsc == rsc) { return stack_stable; } else if (other_p->variant == pe_native) { crm_notice("Cannot migrate %s due to dependency on %s (%s)", rsc->id, other_rsc->id, type); return stack_middle; } else if (other_rsc == rsc->parent) { int mode = 0; GListPtr gIter = NULL; for (gIter = other_rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; if (constraint->score > 0) { mode |= check_stack_element(rsc, constraint->rsc_rh, type); } } return mode; } else if (other_p->variant == pe_group) { crm_notice("Cannot migrate %s due to dependency on group %s (%s)", rsc->id, other_rsc->id, type); return stack_middle; } /* else: >= clone */ /* ## Assumption A depends on clone(B) ## Resource Activity During Move N1 N2 N3 --- --- --- t0 A.stop t1 B.stop B.stop t2 B.start B.start t3 A.start ## Resource Activity During Migration N1 N2 N3 --- --- --- t0 B.start B.start t1 A.stop (1) t2 A.start (2) t3 B.stop B.stop Node 1: Rewritten to be a migrate-to operation Node 2: Rewritten to be a migrate-from operation # Constraints The following constraints already exist in the system. The 'ok' and 'fail' column refers to whether they still hold for migration. a) A.stop -> A.start - ok b) B.stop -> B.start - fail c) A.stop -> B.stop - ok d) B.start -> A.start - ok e) B.stop -> A.start - fail f) A.stop -> B.start - fail ## Scenarios B unchanged - ok B stopping only - fail - possible after fixing 'e' B starting only - fail - possible after fixing 'f' B stoping and starting - fail - constraint 'b' is unfixable B restarting only on N2 - fail - as-per previous only rarer */ /* Only allow migration when the clone is either stable, only starting or only stopping */ return find_clone_activity_on(rsc, other_rsc, NULL, type); } static gboolean at_stack_bottom(resource_t * rsc) { char *key = NULL; action_t *start = NULL; action_t *other = NULL; int mode = stack_stable; GListPtr action_list = NULL; GListPtr gIter = NULL; key = start_key(rsc); action_list = find_actions(rsc->actions, key, NULL); free(key); pe_rsc_trace(rsc, "%s: processing", rsc->id); CRM_CHECK(action_list != NULL, return FALSE); start = action_list->data; g_list_free(action_list); for (gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; resource_t *target = constraint->rsc_rh; pe_rsc_trace(rsc, "Checking %s: %s == %s (%d)", constraint->id, rsc->id, target->id, constraint->score); if (constraint->score > 0) { mode |= check_stack_element(rsc, target, "coloc"); if (mode & stack_middle) { return FALSE; } else if ((mode & stack_stopping) && (mode & stack_starting)) { crm_notice("Cannot migrate %s due to colocation activity (last was %s)", rsc->id, target->id); return FALSE; } } } for (gIter = start->actions_before; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t *) gIter->data; other = other_w->action; if (other_w->type & pe_order_serialize_only) { pe_rsc_trace(rsc, "%s: depends on %s (serialize ordering)", rsc->id, other->uuid); continue; } pe_rsc_trace(rsc, "%s: Checking %s ordering", rsc->id, other->uuid); if (is_set(other->flags, pe_action_optional) == FALSE) { mode |= check_stack_element(rsc, other->rsc, "order"); if (mode & stack_middle) { return FALSE; } else if ((mode & stack_stopping) && (mode & stack_starting)) { crm_notice("Cannot migrate %s due to ordering activity (last was %s)", rsc->id, other->rsc->id); return FALSE; } } } return TRUE; } static action_t * get_first_named_action(resource_t *rsc, const char *action, gboolean only_valid, node_t *current) { action_t *a = NULL; GListPtr action_list = NULL; char *key = generate_op_key(rsc->id, action, 0); action_list = find_actions(rsc->actions, key, current); if (action_list == NULL || action_list->data == NULL) { crm_trace("%s: no %s action", rsc->id, action); free(key); return NULL; } a = action_list->data; g_list_free(action_list); if(only_valid && is_set(a->flags, pe_action_pseudo)) { crm_trace("%s: pseudo", key); a = NULL; } else if(only_valid && is_not_set(a->flags, pe_action_runnable)) { crm_trace("%s: runnable", key); a = NULL; } free(key); return a; } static void MigrateRsc(resource_t * rsc, action_t *stop, action_t *start, pe_working_set_t * data_set, gboolean partial) { action_t *to = NULL; action_t *from = NULL; action_t *then = NULL; action_t *other = NULL; action_t *done = get_pseudo_op(STONITH_DONE, data_set); GListPtr gIter = NULL; const char *value = g_hash_table_lookup(rsc->meta, XML_OP_ATTR_ALLOW_MIGRATE); if (crm_is_true(value) == FALSE) { return; } if (rsc->next_role > RSC_ROLE_SLAVE) { pe_rsc_trace(rsc, "%s: resource role: role=%s", rsc->id, role2text(rsc->next_role)); return; } if(start == NULL || stop == NULL) { pe_rsc_trace(rsc, "%s: not exists %p -> %p", rsc->id, stop, start); return; } else if (start->node == NULL || stop->node == NULL) { pe_rsc_trace(rsc, "%s: no node %p -> %p", rsc->id, stop->node, start->node); return; } else if(is_set(stop->flags, pe_action_optional)) { pe_rsc_trace(rsc, "%s: stop action", rsc->id); return; } else if(is_set(start->flags, pe_action_optional)) { pe_rsc_trace(rsc, "%s: start action", rsc->id); return; } else if (stop->node->details == start->node->details) { pe_rsc_trace(rsc, "%s: not moving %p -> %p", rsc->id, stop->node, start->node); return; } else if (at_stack_bottom(rsc) == FALSE) { pe_rsc_trace(rsc, "%s: not at stack bottom", rsc->id); return; } pe_rsc_trace(rsc, "%s %s -> %s", rsc->id, stop->node->details->uname, start->node->details->uname); if (partial) { pe_rsc_info(rsc, "Completing partial migration of %s from %s to %s", rsc->id, stop->node ? stop->node->details->uname : "unknown", start->node ? start->node->details->uname : "unknown"); } else { pe_rsc_info(rsc, "Migrating %s from %s to %s", rsc->id, stop->node ? stop->node->details->uname : "unknown", start->node ? start->node->details->uname : "unknown"); } /* Preserve the stop to ensure the end state is sane on that node, * Make the start a pseudo op * Create migrate_to, have it depend on everything the stop did * Create migrate_from * *-> migrate_to -> migrate_from -> stop -> start */ update_action_flags(start, pe_action_pseudo); /* easier than trying to delete it from the graph * but perhaps we should have it run anyway */ if (!partial) { to = custom_action(rsc, generate_op_key(rsc->id, RSC_MIGRATE, 0), RSC_MIGRATE, stop->node, FALSE, TRUE, data_set); for (gIter = rsc->dangling_migrations; gIter != NULL; gIter = gIter->next) { node_t *current = (node_t *) gIter->data; action_t *stop = stop_action(rsc, current, FALSE); order_actions(stop, to, pe_order_optional); pe_rsc_trace(rsc, "Ordering migration after cleanup of %s on %s", rsc->id, current->details->uname); } } from = custom_action(rsc, generate_op_key(rsc->id, RSC_MIGRATED, 0), RSC_MIGRATED, start->node, FALSE, TRUE, data_set); /* This is slightly sub-optimal if 'to' fails, but always * run both halves of the migration before terminating the * transition. * * This can be removed if/when we update unpack_rsc_op() to * 'correctly' handle partial migrations. * * Without this, we end up stopping both sides */ from->priority = INFINITY; if (!partial) { order_actions(to, from, pe_order_optional); add_hash_param(to->meta, XML_LRM_ATTR_MIGRATE_SOURCE, stop->node->details->uname); add_hash_param(to->meta, XML_LRM_ATTR_MIGRATE_TARGET, start->node->details->uname); } then = to ? to : from; order_actions(from, stop, pe_order_optional); order_actions(done, then, pe_order_optional); add_hash_param(from->meta, XML_LRM_ATTR_MIGRATE_SOURCE, stop->node->details->uname); add_hash_param(from->meta, XML_LRM_ATTR_MIGRATE_TARGET, start->node->details->uname); /* Create the correct ordering ajustments based on find_clone_activity_on(); */ for (gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; resource_t *target = constraint->rsc_rh; pe_rsc_info(rsc, "Repairing %s: %s == %s (%d)", constraint->id, rsc->id, target->id, constraint->score); if (constraint->score > 0) { int mode = check_stack_element(rsc, target, "coloc"); action_t *clone_stop = find_first_action(target->actions, NULL, RSC_STOP, NULL); action_t *clone_start = find_first_action(target->actions, NULL, RSC_STARTED, NULL); CRM_ASSERT(clone_stop != NULL); CRM_ASSERT(clone_start != NULL); CRM_ASSERT((mode & stack_middle) == 0); CRM_ASSERT(((mode & stack_stopping) && (mode & stack_starting)) == 0); if (mode & stack_stopping) { #if 0 crm_debug("Creating %s.start -> %s.stop ordering", rsc->id, target->id); order_actions(from, clone_stop, pe_order_optional); #endif GListPtr lpc2 = NULL; for (lpc2 = start->actions_before; lpc2 != NULL; lpc2 = lpc2->next) { action_wrapper_t *other_w = (action_wrapper_t *) lpc2->data; /* Needed if the clone's started pseudo-action ever gets printed in the graph */ if (other_w->action == clone_start) { crm_debug("Breaking %s -> %s ordering", other_w->action->uuid, start->uuid); other_w->type = pe_order_none; } } } else if (mode & stack_starting) { #if 0 crm_debug("Creating %s.started -> %s.stop ordering", target->id, rsc->id); order_actions(clone_start, to, pe_order_optional); #endif GListPtr lpc2 = NULL; for (lpc2 = clone_stop->actions_before; lpc2 != NULL; lpc2 = lpc2->next) { action_wrapper_t *other_w = (action_wrapper_t *) lpc2->data; /* Needed if the clone's stop pseudo-action ever gets printed in the graph */ if (other_w->action == stop) { crm_debug("Breaking %s -> %s ordering", other_w->action->uuid, clone_stop->uuid); other_w->type = pe_order_none; } } } } } #if 0 /* Implied now that start/stop are not morphed into migrate ops */ /* Anything that needed stop to complete, now also needs start to have completed */ for (gIter = stop->actions_after; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t *) gIter->data; other = other_w->action; if (is_set(other->flags, pe_action_optional) || other->rsc != NULL) { continue; } crm_debug("Ordering %s before %s (stop)", from->uuid, other->uuid); order_actions(from, other, other_w->type); } #endif /* migrate 'then' action also needs anything that the stop needed to have completed too */ for (gIter = stop->actions_before; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t *) gIter->data; other = other_w->action; if (other->rsc == NULL) { /* nothing */ } else if (is_set(other->flags, pe_action_optional) || other->rsc == rsc || other->rsc == rsc->parent) { continue; } crm_debug("Ordering %s before %s (stop)", other_w->action->uuid, stop->uuid); order_actions(other, then, other_w->type); } /* migrate 'then' action also needs anything that the start needed to have completed too */ for (gIter = start->actions_before; gIter != NULL; gIter = gIter->next) { action_wrapper_t *other_w = (action_wrapper_t *) gIter->data; other = other_w->action; if (other->rsc == NULL) { /* nothing */ } else if (is_set(other->flags, pe_action_optional) || other->rsc == rsc || other->rsc == rsc->parent) { continue; } crm_debug("Ordering %s before %s (start)", other_w->action->uuid, stop->uuid); order_actions(other, then, other_w->type); } } static void ReloadRsc(resource_t * rsc, action_t *stop, action_t *start, pe_working_set_t * data_set) { action_t *action = NULL; action_t *rewrite = NULL; if(is_not_set(rsc->flags, pe_rsc_try_reload)) { return; } else if(is_not_set(stop->flags, pe_action_optional)) { pe_rsc_trace(rsc, "%s: stop action", rsc->id); return; } else if(is_not_set(start->flags, pe_action_optional)) { pe_rsc_trace(rsc, "%s: start action", rsc->id); return; } pe_rsc_trace(rsc, "%s on %s", rsc->id, stop->node->details->uname); action = get_first_named_action(rsc, RSC_PROMOTE, TRUE, NULL); if (action && is_set(action->flags, pe_action_optional) == FALSE) { update_action_flags(action, pe_action_pseudo); } action = get_first_named_action(rsc, RSC_DEMOTE, TRUE, NULL); if (action && is_set(action->flags, pe_action_optional) == FALSE) { rewrite = action; update_action_flags(stop, pe_action_pseudo); } else { rewrite = start; } pe_rsc_info(rsc, "Rewriting %s of %s on %s as a reload", rewrite->task, rsc->id, stop->node->details->uname); set_bit(rsc->flags, pe_rsc_reload); update_action_flags(rewrite, pe_action_optional|pe_action_clear); free(rewrite->uuid); free(rewrite->task); rewrite->task = strdup("reload"); rewrite->uuid = generate_op_key(rsc->id, rewrite->task, 0); } void rsc_migrate_reload(resource_t * rsc, pe_working_set_t * data_set) { GListPtr gIter = NULL; action_t *stop = NULL; action_t *start = NULL; gboolean partial = FALSE; if (rsc->children) { for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) { resource_t *child_rsc = (resource_t *) gIter->data; rsc_migrate_reload(child_rsc, data_set); } return; } else if (rsc->variant > pe_native) { return; } pe_rsc_trace(rsc, "Processing %s", rsc->id); if (rsc->partial_migration_target) { start = get_first_named_action(rsc, RSC_START, TRUE, rsc->partial_migration_target); stop = get_first_named_action(rsc, RSC_STOP, TRUE, rsc->partial_migration_source); if (start && stop) { partial = TRUE; } } pe_rsc_trace(rsc, "%s %s %p", rsc->id, partial?"partial":"full", stop); if (!partial) { stop = get_first_named_action(rsc, RSC_STOP, TRUE, rsc->running_on ? rsc->running_on->data : NULL); start = get_first_named_action(rsc, RSC_START, TRUE, NULL); } if (is_not_set(rsc->flags, pe_rsc_managed) || is_set(rsc->flags, pe_rsc_failed) || is_set(rsc->flags, pe_rsc_start_pending) || rsc->next_role < RSC_ROLE_STARTED || ((g_list_length(rsc->running_on) != 1) && !partial)) { pe_rsc_trace(rsc, "%s: general resource state: flags=0x%.16llx", rsc->id, rsc->flags); return; } if(stop == NULL) { return; } else if (is_set(stop->flags, pe_action_optional) && is_set(rsc->flags, pe_rsc_try_reload)) { ReloadRsc(rsc, stop, start, data_set); } else if(is_not_set(stop->flags, pe_action_optional)) { MigrateRsc(rsc, stop, start, data_set, partial); } } void native_append_meta(resource_t * rsc, xmlNode * xml) { char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_INCARNATION); if (value) { char *name = NULL; name = crm_meta_name(XML_RSC_ATTR_INCARNATION); crm_xml_add(xml, name, value); free(name); } } diff --git a/pengine/test10/797.dot b/pengine/test10/797.dot index 85cfb9e5f1..6d56b26972 100644 --- a/pengine/test10/797.dot +++ b/pengine/test10/797.dot @@ -1,112 +1,110 @@ digraph "g" { "DcIPaddr_delete_0 c001n03" -> "DcIPaddr_start_0 c001n03" [ style = dashed] -"DcIPaddr_delete_0 c001n03" -> "lrm_refresh c001n03" [ style = bold] "DcIPaddr_delete_0 c001n03" [ style=bold color="green" fontcolor="black" ] "DcIPaddr_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "DcIPaddr_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "DcIPaddr_monitor_0 c001n02" -> "probe_complete c001n02" [ style = bold] "DcIPaddr_monitor_0 c001n02" [ style=bold color="green" fontcolor="black" ] "DcIPaddr_monitor_5000 c001n03" [ style=dashed color="red" fontcolor="black"] "DcIPaddr_start_0 c001n03" -> "DcIPaddr_monitor_5000 c001n03" [ style = dashed] "DcIPaddr_start_0 c001n03" [ style=dashed color="red" fontcolor="black" ] "DcIPaddr_stop_0 c001n03" -> "DcIPaddr_delete_0 c001n03" [ style = bold] "DcIPaddr_stop_0 c001n03" -> "DcIPaddr_start_0 c001n03" [ style = dashed] "DcIPaddr_stop_0 c001n03" -> "all_stopped" [ style = bold] "DcIPaddr_stop_0 c001n03" [ style=bold color="green" fontcolor="black" ] "DoFencing_running_0" [ style=bold color="green" fontcolor="orange" ] "DoFencing_start_0" -> "DoFencing_running_0" [ style = bold] "DoFencing_start_0" -> "child_DoFencing:0_start_0 c001n01" [ style = bold] "DoFencing_start_0" [ style=bold color="green" fontcolor="orange" ] "DoFencing_stop_0" -> "DoFencing_stopped_0" [ style = bold] "DoFencing_stop_0" -> "child_DoFencing:0_stop_0 c001n01" [ style = bold] "DoFencing_stop_0" -> "child_DoFencing:0_stop_0 c001n03" [ style = bold] "DoFencing_stop_0" -> "child_DoFencing:1_stop_0 c001n02" [ style = bold] "DoFencing_stop_0" [ style=bold color="green" fontcolor="orange" ] "DoFencing_stopped_0" -> "DoFencing_start_0" [ style = bold] "DoFencing_stopped_0" [ style=bold color="green" fontcolor="orange" ] "all_stopped" [ style=bold color="green" fontcolor="orange" ] "child_DoFencing:0_monitor_5000 c001n01" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:0_start_0 c001n01" -> "DoFencing_running_0" [ style = bold] "child_DoFencing:0_start_0 c001n01" -> "child_DoFencing:0_monitor_5000 c001n01" [ style = bold] "child_DoFencing:0_start_0 c001n01" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:0_stop_0 c001n01" -> "DoFencing_stopped_0" [ style = bold] "child_DoFencing:0_stop_0 c001n01" -> "all_stopped" [ style = bold] "child_DoFencing:0_stop_0 c001n01" -> "child_DoFencing:0_start_0 c001n01" [ style = bold] "child_DoFencing:0_stop_0 c001n01" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:0_stop_0 c001n03" -> "DoFencing_stopped_0" [ style = bold] "child_DoFencing:0_stop_0 c001n03" -> "all_stopped" [ style = bold] "child_DoFencing:0_stop_0 c001n03" -> "child_DoFencing:0_start_0 c001n01" [ style = bold] "child_DoFencing:0_stop_0 c001n03" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:1_stop_0 c001n02" -> "DoFencing_stopped_0" [ style = bold] "child_DoFencing:1_stop_0 c001n02" -> "all_stopped" [ style = bold] "child_DoFencing:1_stop_0 c001n02" -> "do_shutdown c001n02" [ style = bold] "child_DoFencing:1_stop_0 c001n02" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:2_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "child_DoFencing:2_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:3_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "child_DoFencing:3_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:3_monitor_0 c001n02" -> "probe_complete c001n02" [ style = bold] "child_DoFencing:3_monitor_0 c001n02" [ style=bold color="green" fontcolor="black" ] "child_DoFencing:3_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "child_DoFencing:3_monitor_0 c001n03" [ style=bold color="green" fontcolor="black" ] "do_shutdown c001n02" [ style=bold color="green" fontcolor="black" ] -"lrm_refresh c001n03" [ style=bold color="green" fontcolor="black" ] "probe_complete c001n01" -> "probe_complete" [ style = bold] "probe_complete c001n01" [ style=bold color="green" fontcolor="black" ] "probe_complete c001n02" -> "probe_complete" [ style = bold] "probe_complete c001n02" [ style=bold color="green" fontcolor="black" ] "probe_complete c001n03" -> "probe_complete" [ style = bold] "probe_complete c001n03" [ style=bold color="green" fontcolor="black" ] "probe_complete" -> "DcIPaddr_start_0 c001n03" [ style = dashed] "probe_complete" -> "DcIPaddr_stop_0 c001n03" [ style = bold] "probe_complete" -> "child_DoFencing:0_start_0 c001n01" [ style = bold] "probe_complete" -> "child_DoFencing:0_stop_0 c001n01" [ style = bold] "probe_complete" -> "child_DoFencing:0_stop_0 c001n03" [ style = bold] "probe_complete" -> "rsc_c001n01_start_0 c001n01" [ style = dashed] "probe_complete" -> "rsc_c001n01_stop_0 c001n01" [ style = bold] "probe_complete" -> "rsc_c001n02_start_0 c001n01" [ style = dashed] "probe_complete" -> "rsc_c001n03_start_0 c001n03" [ style = dashed] "probe_complete" -> "rsc_c001n03_stop_0 c001n03" [ style = bold] "probe_complete" -> "rsc_c001n08_start_0 c001n01" [ style = dashed] "probe_complete" [ style=bold color="green" fontcolor="orange" ] "rsc_c001n01_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "rsc_c001n01_monitor_0 c001n03" [ style=bold color="green" fontcolor="black" ] "rsc_c001n01_monitor_5000 c001n01" [ style=dashed color="red" fontcolor="black"] "rsc_c001n01_start_0 c001n01" -> "rsc_c001n01_monitor_5000 c001n01" [ style = dashed] "rsc_c001n01_start_0 c001n01" [ style=dashed color="red" fontcolor="black" ] "rsc_c001n01_stop_0 c001n01" -> "all_stopped" [ style = bold] "rsc_c001n01_stop_0 c001n01" -> "rsc_c001n01_start_0 c001n01" [ style = dashed] "rsc_c001n01_stop_0 c001n01" [ style=bold color="green" fontcolor="black" ] "rsc_c001n02_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "rsc_c001n02_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "rsc_c001n02_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "rsc_c001n02_monitor_0 c001n03" [ style=bold color="green" fontcolor="black" ] "rsc_c001n02_monitor_5000 c001n01" [ style=dashed color="red" fontcolor="black" ] "rsc_c001n02_start_0 c001n01" -> "rsc_c001n02_monitor_5000 c001n01" [ style = dashed] "rsc_c001n02_start_0 c001n01" [ style=dashed color="red" fontcolor="black" ] "rsc_c001n02_stop_0 c001n02" -> "all_stopped" [ style = bold] "rsc_c001n02_stop_0 c001n02" -> "do_shutdown c001n02" [ style = bold] "rsc_c001n02_stop_0 c001n02" -> "rsc_c001n02_start_0 c001n01" [ style = dashed] "rsc_c001n02_stop_0 c001n02" [ style=bold color="green" fontcolor="black" ] "rsc_c001n03_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "rsc_c001n03_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "rsc_c001n03_monitor_0 c001n02" -> "probe_complete c001n02" [ style = bold] "rsc_c001n03_monitor_0 c001n02" [ style=bold color="green" fontcolor="black" ] "rsc_c001n03_monitor_5000 c001n03" [ style=dashed color="red" fontcolor="black"] "rsc_c001n03_start_0 c001n03" -> "rsc_c001n03_monitor_5000 c001n03" [ style = dashed] "rsc_c001n03_start_0 c001n03" [ style=dashed color="red" fontcolor="black" ] "rsc_c001n03_stop_0 c001n03" -> "all_stopped" [ style = bold] "rsc_c001n03_stop_0 c001n03" -> "rsc_c001n03_start_0 c001n03" [ style = dashed] "rsc_c001n03_stop_0 c001n03" [ style=bold color="green" fontcolor="black" ] "rsc_c001n08_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "rsc_c001n08_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "rsc_c001n08_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "rsc_c001n08_monitor_0 c001n03" [ style=bold color="green" fontcolor="black" ] "rsc_c001n08_monitor_5000 c001n01" [ style=dashed color="red" fontcolor="black" ] "rsc_c001n08_start_0 c001n01" -> "rsc_c001n08_monitor_5000 c001n01" [ style = dashed] "rsc_c001n08_start_0 c001n01" [ style=dashed color="red" fontcolor="black" ] "rsc_c001n08_stop_0 c001n02" -> "all_stopped" [ style = bold] "rsc_c001n08_stop_0 c001n02" -> "do_shutdown c001n02" [ style = bold] "rsc_c001n08_stop_0 c001n02" -> "rsc_c001n08_start_0 c001n01" [ style = dashed] "rsc_c001n08_stop_0 c001n02" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/797.exp b/pengine/test10/797.exp index 7dcfb1ba5c..cac962db57 100644 --- a/pengine/test10/797.exp +++ b/pengine/test10/797.exp @@ -1,473 +1,461 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - + - + - + - + - + - + - + diff --git a/pengine/test10/797.summary b/pengine/test10/797.summary index 1857998c34..c49e1b9209 100644 --- a/pengine/test10/797.summary +++ b/pengine/test10/797.summary @@ -1,74 +1,73 @@ Current cluster status: Node c001n08 (6427cb5a-c7a5-4bdf-9892-a04ce56f4e6b): UNCLEAN (offline) Online: [ c001n01 c001n02 c001n03 ] DcIPaddr (ocf::heartbeat:IPaddr): Started c001n03 rsc_c001n08 (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n02 (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n03 (ocf::heartbeat:IPaddr): Started c001n03 rsc_c001n01 (ocf::heartbeat:IPaddr): Started c001n01 Clone Set: DoFencing [child_DoFencing] (unique) child_DoFencing:0 (stonith:ssh): Started [ c001n01 c001n03 ] child_DoFencing:1 (stonith:ssh): Started c001n02 child_DoFencing:2 (stonith:ssh): Started c001n03 child_DoFencing:3 (stonith:ssh): Stopped Transition Summary: * Stop DcIPaddr (Started c001n03) * Stop rsc_c001n08 (Started c001n01) * Stop rsc_c001n02 (Started c001n01) * Stop rsc_c001n03 (Started c001n03) * Stop rsc_c001n01 (Started c001n01) * Restart child_DoFencing:0 (Started c001n01) * Stop child_DoFencing:1 (c001n02) Executing cluster transition: * Resource action: DcIPaddr monitor on c001n02 * Resource action: DcIPaddr monitor on c001n01 * Resource action: rsc_c001n08 stop on c001n02 * Resource action: rsc_c001n08 monitor on c001n03 * Resource action: rsc_c001n08 monitor on c001n01 * Resource action: rsc_c001n02 stop on c001n02 * Resource action: rsc_c001n02 monitor on c001n03 * Resource action: rsc_c001n02 monitor on c001n01 * Resource action: rsc_c001n03 monitor on c001n02 * Resource action: rsc_c001n03 monitor on c001n01 * Resource action: rsc_c001n01 monitor on c001n03 * Resource action: child_DoFencing:2 monitor on c001n01 * Resource action: child_DoFencing:3 monitor on c001n03 * Resource action: child_DoFencing:3 monitor on c001n02 * Resource action: child_DoFencing:3 monitor on c001n01 * Pseudo action: DoFencing_stop_0 * Pseudo action: probe_complete * Resource action: DcIPaddr stop on c001n03 * Resource action: rsc_c001n03 stop on c001n03 * Resource action: rsc_c001n01 stop on c001n01 * Resource action: child_DoFencing:0 stop on c001n03 * Resource action: child_DoFencing:0 stop on c001n01 * Resource action: child_DoFencing:1 stop on c001n02 * Pseudo action: DoFencing_stopped_0 * Pseudo action: DoFencing_start_0 * Cluster action: do_shutdown on c001n02 * Pseudo action: all_stopped * Resource action: DcIPaddr delete on c001n03 * Resource action: child_DoFencing:0 start on c001n01 * Resource action: child_DoFencing:0 monitor=5000 on c001n01 * Pseudo action: DoFencing_running_0 - * Cluster action: lrm_refresh on c001n03 Revised cluster status: Node c001n08 (6427cb5a-c7a5-4bdf-9892-a04ce56f4e6b): UNCLEAN (offline) Online: [ c001n01 c001n02 c001n03 ] - DcIPaddr (ocf::heartbeat:IPaddr): Started c001n03 + DcIPaddr (ocf::heartbeat:IPaddr): Stopped rsc_c001n08 (ocf::heartbeat:IPaddr): Stopped rsc_c001n02 (ocf::heartbeat:IPaddr): Stopped rsc_c001n03 (ocf::heartbeat:IPaddr): Stopped rsc_c001n01 (ocf::heartbeat:IPaddr): Stopped Clone Set: DoFencing [child_DoFencing] (unique) child_DoFencing:0 (stonith:ssh): Started c001n01 child_DoFencing:1 (stonith:ssh): Stopped child_DoFencing:2 (stonith:ssh): Started c001n03 child_DoFencing:3 (stonith:ssh): Stopped diff --git a/pengine/test10/bug-1718.dot b/pengine/test10/bug-1718.dot index 8b547bf977..aa392d82b5 100644 --- a/pengine/test10/bug-1718.dot +++ b/pengine/test10/bug-1718.dot @@ -1,28 +1,33 @@ digraph "g" { +"Apache_delete_0 biggame.ds9" [ style=bold color="green" fontcolor="black"] +"Apache_delete_0 heartbeat.ds9" [ style=bold color="green" fontcolor="black"] +"Apache_delete_0 ops.ds9" [ style=bold color="green" fontcolor="black"] +"OpenVPN_IP_delete_0 heartbeat.ds9" [ style=bold color="green" fontcolor="black"] +"OpenVPN_IP_delete_0 ops.ds9" [ style=bold color="green" fontcolor="black"] "all_stopped" [ style=bold color="green" fontcolor="orange" ] "group_fUN_running_0" [ style=dashed color="red" fontcolor="orange" ] "group_fUN_start_0" -> "group_fUN_running_0" [ style = dashed] "group_fUN_start_0" -> "resource_IP3_start_0 ops.ds9" [ style = dashed] "group_fUN_start_0" -> "resource_dummy_start_0 ops.ds9" [ style = dashed] "group_fUN_start_0" [ style=dashed color="red" fontcolor="orange" ] "group_fUN_stop_0" -> "group_fUN_stopped_0" [ style = bold] "group_fUN_stop_0" -> "resource_IP3_stop_0 ops.ds9" [ style = bold] "group_fUN_stop_0" -> "resource_dummy_stop_0 ops.ds9" [ style = bold] "group_fUN_stop_0" [ style=bold color="green" fontcolor="orange" ] "group_fUN_stopped_0" -> "group_fUN_start_0" [ style = dashed] "group_fUN_stopped_0" [ style=bold color="green" fontcolor="orange" ] "resource_IP3_start_0 ops.ds9" -> "group_fUN_running_0" [ style = dashed] "resource_IP3_start_0 ops.ds9" -> "resource_dummy_start_0 ops.ds9" [ style = dashed] "resource_IP3_start_0 ops.ds9" [ style=dashed color="red" fontcolor="black" ] "resource_IP3_stop_0 ops.ds9" -> "all_stopped" [ style = bold] "resource_IP3_stop_0 ops.ds9" -> "group_fUN_stopped_0" [ style = bold] "resource_IP3_stop_0 ops.ds9" -> "resource_IP3_start_0 ops.ds9" [ style = dashed] "resource_IP3_stop_0 ops.ds9" [ style=bold color="green" fontcolor="black" ] "resource_dummy_start_0 ops.ds9" -> "group_fUN_running_0" [ style = dashed] "resource_dummy_start_0 ops.ds9" [ style=dashed color="red" fontcolor="black" ] "resource_dummy_stop_0 ops.ds9" -> "all_stopped" [ style = bold] "resource_dummy_stop_0 ops.ds9" -> "group_fUN_stopped_0" [ style = bold] "resource_dummy_stop_0 ops.ds9" -> "resource_IP3_stop_0 ops.ds9" [ style = bold] "resource_dummy_stop_0 ops.ds9" -> "resource_dummy_start_0 ops.ds9" [ style = dashed] "resource_dummy_stop_0 ops.ds9" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/bug-1718.exp b/pengine/test10/bug-1718.exp index 2992c72c49..6d327319af 100644 --- a/pengine/test10/bug-1718.exp +++ b/pengine/test10/bug-1718.exp @@ -1,73 +1,118 @@ - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + diff --git a/pengine/test10/bug-1718.summary b/pengine/test10/bug-1718.summary index 3bf4caeef2..d9a0cccc70 100644 --- a/pengine/test10/bug-1718.summary +++ b/pengine/test10/bug-1718.summary @@ -1,36 +1,41 @@ Current cluster status: Online: [ biggame.ds9 heartbeat.ds9 ops.ds9 ] OFFLINE: [ defiant.ds9 warbird.ds9 ] Resource Group: Web_Group Apache_IP (ocf::heartbeat:IPaddr): Started heartbeat.ds9 resource_IP2 (ocf::heartbeat:IPaddr): Stopped resource_dummyweb (ocf::heartbeat:Dummy): Stopped Resource Group: group_fUN resource_IP3 (ocf::heartbeat:IPaddr): Started ops.ds9 resource_dummy (ocf::heartbeat:Dummy): Started ops.ds9 Transition Summary: * Stop resource_IP3 (Started ops.ds9) * Stop resource_dummy (Started ops.ds9) Executing cluster transition: * Pseudo action: group_fUN_stop_0 * Resource action: resource_dummy stop on ops.ds9 + * Resource action: OpenVPN_IP delete on ops.ds9 + * Resource action: OpenVPN_IP delete on heartbeat.ds9 + * Resource action: Apache delete on biggame.ds9 + * Resource action: Apache delete on ops.ds9 + * Resource action: Apache delete on heartbeat.ds9 * Resource action: resource_IP3 stop on ops.ds9 * Pseudo action: all_stopped * Pseudo action: group_fUN_stopped_0 Revised cluster status: Online: [ biggame.ds9 heartbeat.ds9 ops.ds9 ] OFFLINE: [ defiant.ds9 warbird.ds9 ] Resource Group: Web_Group Apache_IP (ocf::heartbeat:IPaddr): Started heartbeat.ds9 resource_IP2 (ocf::heartbeat:IPaddr): Stopped resource_dummyweb (ocf::heartbeat:Dummy): Stopped Resource Group: group_fUN resource_IP3 (ocf::heartbeat:IPaddr): Stopped resource_dummy (ocf::heartbeat:Dummy): Stopped diff --git a/pengine/test10/bug-5059.dot b/pengine/test10/bug-5059.dot index ea5851c528..97e3992e8a 100644 --- a/pengine/test10/bug-5059.dot +++ b/pengine/test10/bug-5059.dot @@ -1,104 +1,106 @@ digraph "g" { "g_stateful:0_promote_0" -> "p_stateful1:0_promote_0 gluster01.h" [ style = bold] "g_stateful:0_promote_0" -> "p_stateful2:0_promote_0 gluster01.h" [ style = bold] "g_stateful:0_promote_0" [ style=bold color="green" fontcolor="orange"] "g_stateful:0_promoted_0" -> "ms_stateful_promoted_0" [ style = bold] "g_stateful:0_promoted_0" [ style=bold color="green" fontcolor="orange"] "g_stateful:0_running_0" -> "ms_stateful_running_0" [ style = bold] "g_stateful:0_running_0" [ style=bold color="green" fontcolor="orange"] "g_stateful:0_start_0" -> "g_stateful:0_running_0" [ style = bold] "g_stateful:0_start_0" -> "p_stateful2:0_start_0 gluster01.h" [ style = bold] "g_stateful:0_start_0" [ style=bold color="green" fontcolor="orange"] "g_stateful:1_running_0" -> "ms_stateful_running_0" [ style = bold] "g_stateful:1_running_0" [ style=bold color="green" fontcolor="orange"] "g_stateful:1_start_0" -> "g_stateful:1_running_0" [ style = bold] "g_stateful:1_start_0" -> "p_stateful2:1_start_0 gluster02.h" [ style = bold] "g_stateful:1_start_0" [ style=bold color="green" fontcolor="orange"] +"ipsrc2_delete_0 gluster02.h" [ style=bold color="green" fontcolor="black"] +"iptest_delete_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "ms_stateful_confirmed-post_notify_promoted_0" -> "p_stateful1:1_monitor_10000 gluster02.h" [ style = bold] "ms_stateful_confirmed-post_notify_promoted_0" -> "p_stateful2:1_monitor_10000 gluster02.h" [ style = bold] "ms_stateful_confirmed-post_notify_promoted_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_confirmed-post_notify_running_0" -> "ms_stateful_pre_notify_promote_0" [ style = bold] "ms_stateful_confirmed-post_notify_running_0" -> "p_stateful1:1_monitor_10000 gluster02.h" [ style = bold] "ms_stateful_confirmed-post_notify_running_0" -> "p_stateful2:1_monitor_10000 gluster02.h" [ style = bold] "ms_stateful_confirmed-post_notify_running_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_confirmed-pre_notify_promote_0" -> "ms_stateful_post_notify_promoted_0" [ style = bold] "ms_stateful_confirmed-pre_notify_promote_0" -> "ms_stateful_promote_0" [ style = bold] "ms_stateful_confirmed-pre_notify_promote_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_confirmed-pre_notify_start_0" -> "ms_stateful_post_notify_running_0" [ style = bold] "ms_stateful_confirmed-pre_notify_start_0" -> "ms_stateful_start_0" [ style = bold] "ms_stateful_confirmed-pre_notify_start_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_post_notify_promoted_0" -> "ms_stateful_confirmed-post_notify_promoted_0" [ style = bold] "ms_stateful_post_notify_promoted_0" -> "p_stateful1:0_post_notify_promote_0 gluster01.h" [ style = bold] "ms_stateful_post_notify_promoted_0" -> "p_stateful1:1_post_notify_promote_0 gluster02.h" [ style = bold] "ms_stateful_post_notify_promoted_0" -> "p_stateful2:0_post_notify_promote_0 gluster01.h" [ style = bold] "ms_stateful_post_notify_promoted_0" -> "p_stateful2:1_post_notify_promote_0 gluster02.h" [ style = bold] "ms_stateful_post_notify_promoted_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_post_notify_running_0" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "ms_stateful_post_notify_running_0" -> "p_stateful1:0_post_notify_start_0 gluster01.h" [ style = bold] "ms_stateful_post_notify_running_0" -> "p_stateful1:1_post_notify_start_0 gluster02.h" [ style = bold] "ms_stateful_post_notify_running_0" -> "p_stateful2:0_post_notify_start_0 gluster01.h" [ style = bold] "ms_stateful_post_notify_running_0" -> "p_stateful2:1_post_notify_start_0 gluster02.h" [ style = bold] "ms_stateful_post_notify_running_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_pre_notify_promote_0" -> "ms_stateful_confirmed-pre_notify_promote_0" [ style = bold] "ms_stateful_pre_notify_promote_0" -> "p_stateful1:0_pre_notify_promote_0 gluster01.h" [ style = bold] "ms_stateful_pre_notify_promote_0" -> "p_stateful1:1_pre_notify_promote_0 gluster02.h" [ style = bold] "ms_stateful_pre_notify_promote_0" -> "p_stateful2:0_pre_notify_promote_0 gluster01.h" [ style = bold] "ms_stateful_pre_notify_promote_0" -> "p_stateful2:1_pre_notify_promote_0 gluster02.h" [ style = bold] "ms_stateful_pre_notify_promote_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_pre_notify_start_0" -> "ms_stateful_confirmed-pre_notify_start_0" [ style = bold] "ms_stateful_pre_notify_start_0" -> "p_stateful1:0_pre_notify_start_0 gluster01.h" [ style = bold] "ms_stateful_pre_notify_start_0" -> "p_stateful1:1_pre_notify_start_0 gluster02.h" [ style = bold] "ms_stateful_pre_notify_start_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_promote_0" -> "g_stateful:0_promote_0" [ style = bold] "ms_stateful_promote_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_promoted_0" -> "ms_stateful_post_notify_promoted_0" [ style = bold] "ms_stateful_promoted_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_running_0" -> "ms_stateful_post_notify_running_0" [ style = bold] "ms_stateful_running_0" -> "ms_stateful_promote_0" [ style = bold] "ms_stateful_running_0" [ style=bold color="green" fontcolor="orange"] "ms_stateful_start_0" -> "g_stateful:0_start_0" [ style = bold] "ms_stateful_start_0" -> "g_stateful:1_start_0" [ style = bold] "ms_stateful_start_0" -> "ms_stateful_running_0" [ style = bold] "ms_stateful_start_0" [ style=bold color="green" fontcolor="orange"] "p_stateful1:0_post_notify_promote_0 gluster01.h" -> "ms_stateful_confirmed-post_notify_promoted_0" [ style = bold] "p_stateful1:0_post_notify_promote_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:0_post_notify_start_0 gluster01.h" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "p_stateful1:0_post_notify_start_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:0_pre_notify_promote_0 gluster01.h" -> "ms_stateful_confirmed-pre_notify_promote_0" [ style = bold] "p_stateful1:0_pre_notify_promote_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:0_pre_notify_start_0 gluster01.h" -> "ms_stateful_confirmed-pre_notify_start_0" [ style = bold] "p_stateful1:0_pre_notify_start_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:0_promote_0 gluster01.h" -> "g_stateful:0_promoted_0" [ style = bold] "p_stateful1:0_promote_0 gluster01.h" -> "p_stateful2:0_promote_0 gluster01.h" [ style = bold] "p_stateful1:0_promote_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:1_monitor_10000 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:1_post_notify_promote_0 gluster02.h" -> "ms_stateful_confirmed-post_notify_promoted_0" [ style = bold] "p_stateful1:1_post_notify_promote_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:1_post_notify_start_0 gluster02.h" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "p_stateful1:1_post_notify_start_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:1_pre_notify_promote_0 gluster02.h" -> "ms_stateful_confirmed-pre_notify_promote_0" [ style = bold] "p_stateful1:1_pre_notify_promote_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful1:1_pre_notify_start_0 gluster02.h" -> "ms_stateful_confirmed-pre_notify_start_0" [ style = bold] "p_stateful1:1_pre_notify_start_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:0_post_notify_promote_0 gluster01.h" -> "ms_stateful_confirmed-post_notify_promoted_0" [ style = bold] "p_stateful2:0_post_notify_promote_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:0_post_notify_start_0 gluster01.h" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "p_stateful2:0_post_notify_start_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:0_pre_notify_promote_0 gluster01.h" -> "ms_stateful_confirmed-pre_notify_promote_0" [ style = bold] "p_stateful2:0_pre_notify_promote_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:0_promote_0 gluster01.h" -> "g_stateful:0_promoted_0" [ style = bold] "p_stateful2:0_promote_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:0_start_0 gluster01.h" -> "g_stateful:0_running_0" [ style = bold] "p_stateful2:0_start_0 gluster01.h" -> "p_stateful2:0_promote_0 gluster01.h" [ style = bold] "p_stateful2:0_start_0 gluster01.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:1_monitor_10000 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:1_post_notify_promote_0 gluster02.h" -> "ms_stateful_confirmed-post_notify_promoted_0" [ style = bold] "p_stateful2:1_post_notify_promote_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:1_post_notify_start_0 gluster02.h" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "p_stateful2:1_post_notify_start_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:1_pre_notify_promote_0 gluster02.h" -> "ms_stateful_confirmed-pre_notify_promote_0" [ style = bold] "p_stateful2:1_pre_notify_promote_0 gluster02.h" [ style=bold color="green" fontcolor="black"] "p_stateful2:1_start_0 gluster02.h" -> "g_stateful:1_running_0" [ style = bold] "p_stateful2:1_start_0 gluster02.h" -> "p_stateful2:1_monitor_10000 gluster02.h" [ style = bold] "p_stateful2:1_start_0 gluster02.h" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/bug-5059.exp b/pengine/test10/bug-5059.exp index 6fdbee8298..b86b87edfb 100644 --- a/pengine/test10/bug-5059.exp +++ b/pengine/test10/bug-5059.exp @@ -1,556 +1,574 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + diff --git a/pengine/test10/bug-5059.summary b/pengine/test10/bug-5059.summary index 25a013f528..b854e43563 100644 --- a/pengine/test10/bug-5059.summary +++ b/pengine/test10/bug-5059.summary @@ -1,73 +1,75 @@ Current cluster status: Node gluster03.h: standby Online: [ gluster01.h gluster02.h ] OFFLINE: [ gluster04.h ] Master/Slave Set: ms_stateful [g_stateful] Resource Group: g_stateful:0 p_stateful1:0 (ocf::pacemaker:Stateful): Started gluster01.h p_stateful2:0 (ocf::pacemaker:Stateful): Stopped Resource Group: g_stateful:1 p_stateful1:1 (ocf::pacemaker:Stateful): Started gluster02.h p_stateful2:1 (ocf::pacemaker:Stateful): Stopped Clone Set: c_dummy [p_dummy1] Started: [ gluster01.h gluster02.h ] Transition Summary: * Promote p_stateful1:0 (Slave -> Master gluster01.h) * Start p_stateful2:0 (gluster01.h) * Promote p_stateful2:0 (Stopped -> Master gluster01.h) * Start p_stateful2:1 (gluster02.h) Executing cluster transition: * Pseudo action: ms_stateful_pre_notify_start_0 + * Resource action: iptest delete on gluster02.h + * Resource action: ipsrc2 delete on gluster02.h * Resource action: p_stateful1:0 notify on gluster01.h * Resource action: p_stateful1:1 notify on gluster02.h * Pseudo action: ms_stateful_confirmed-pre_notify_start_0 * Pseudo action: ms_stateful_start_0 * Pseudo action: g_stateful:0_start_0 * Resource action: p_stateful2:0 start on gluster01.h * Pseudo action: g_stateful:1_start_0 * Resource action: p_stateful2:1 start on gluster02.h * Pseudo action: g_stateful:0_running_0 * Pseudo action: g_stateful:1_running_0 * Pseudo action: ms_stateful_running_0 * Pseudo action: ms_stateful_post_notify_running_0 * Resource action: p_stateful1:0 notify on gluster01.h * Resource action: p_stateful2:0 notify on gluster01.h * Resource action: p_stateful1:1 notify on gluster02.h * Resource action: p_stateful2:1 notify on gluster02.h * Pseudo action: ms_stateful_confirmed-post_notify_running_0 * Pseudo action: ms_stateful_pre_notify_promote_0 * Resource action: p_stateful1:0 notify on gluster01.h * Resource action: p_stateful2:0 notify on gluster01.h * Resource action: p_stateful1:1 notify on gluster02.h * Resource action: p_stateful2:1 notify on gluster02.h * Pseudo action: ms_stateful_confirmed-pre_notify_promote_0 * Pseudo action: ms_stateful_promote_0 * Pseudo action: g_stateful:0_promote_0 * Resource action: p_stateful1:0 promote on gluster01.h * Resource action: p_stateful2:0 promote on gluster01.h * Pseudo action: g_stateful:0_promoted_0 * Pseudo action: ms_stateful_promoted_0 * Pseudo action: ms_stateful_post_notify_promoted_0 * Resource action: p_stateful1:0 notify on gluster01.h * Resource action: p_stateful2:0 notify on gluster01.h * Resource action: p_stateful1:1 notify on gluster02.h * Resource action: p_stateful2:1 notify on gluster02.h * Pseudo action: ms_stateful_confirmed-post_notify_promoted_0 * Resource action: p_stateful1:1 monitor=10000 on gluster02.h * Resource action: p_stateful2:1 monitor=10000 on gluster02.h Revised cluster status: Node gluster03.h: standby Online: [ gluster01.h gluster02.h ] OFFLINE: [ gluster04.h ] Master/Slave Set: ms_stateful [g_stateful] Masters: [ gluster01.h ] Slaves: [ gluster02.h ] Clone Set: c_dummy [p_dummy1] Started: [ gluster01.h gluster02.h ] diff --git a/pengine/test10/bug-lf-2361.dot b/pengine/test10/bug-lf-2361.dot index db5da1b24f..60309150fd 100644 --- a/pengine/test10/bug-lf-2361.dot +++ b/pengine/test10/bug-lf-2361.dot @@ -1,35 +1,40 @@ digraph "g" { "cl_dummy2_running_0" [ style=dashed color="red" fontcolor="orange" ] "cl_dummy2_start_0" -> "cl_dummy2_running_0" [ style = dashed] "cl_dummy2_start_0" -> "dummy2:0_start_0 alice.demo" [ style = dashed] "cl_dummy2_start_0" -> "dummy2:1_start_0 bob.demo" [ style = dashed] "cl_dummy2_start_0" [ style=dashed color="red" fontcolor="orange" ] "dummy2:0_start_0 alice.demo" -> "cl_dummy2_running_0" [ style = dashed] "dummy2:0_start_0 alice.demo" [ style=dashed color="red" fontcolor="black" ] "dummy2:1_start_0 bob.demo" -> "cl_dummy2_running_0" [ style = dashed] "dummy2:1_start_0 bob.demo" [ style=dashed color="red" fontcolor="black" ] "ms_stateful_confirmed-post_notify_running_0" [ style=bold color="green" fontcolor="orange" ] "ms_stateful_confirmed-pre_notify_start_0" -> "ms_stateful_post_notify_running_0" [ style = bold] "ms_stateful_confirmed-pre_notify_start_0" -> "ms_stateful_start_0" [ style = bold] "ms_stateful_confirmed-pre_notify_start_0" [ style=bold color="green" fontcolor="orange" ] "ms_stateful_post_notify_running_0" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "ms_stateful_post_notify_running_0" -> "stateful:0_post_notify_start_0 alice.demo" [ style = bold] "ms_stateful_post_notify_running_0" -> "stateful:1_post_notify_start_0 bob.demo" [ style = bold] "ms_stateful_post_notify_running_0" [ style=bold color="green" fontcolor="orange" ] "ms_stateful_pre_notify_start_0" -> "ms_stateful_confirmed-pre_notify_start_0" [ style = bold] "ms_stateful_pre_notify_start_0" [ style=bold color="green" fontcolor="orange" ] "ms_stateful_running_0" -> "ms_stateful_post_notify_running_0" [ style = bold] "ms_stateful_running_0" [ style=bold color="green" fontcolor="orange" ] "ms_stateful_start_0" -> "ms_stateful_running_0" [ style = bold] "ms_stateful_start_0" -> "stateful:0_start_0 alice.demo" [ style = bold] "ms_stateful_start_0" -> "stateful:1_start_0 bob.demo" [ style = bold] "ms_stateful_start_0" [ style=bold color="green" fontcolor="orange" ] +"service1_delete_0 alice.demo" [ style=bold color="green" fontcolor="black"] +"service1_delete_0 bob.demo" [ style=bold color="green" fontcolor="black"] +"service2:0_delete_0 alice.demo" [ style=bold color="green" fontcolor="black"] +"service2:0_delete_0 bob.demo" [ style=bold color="green" fontcolor="black"] +"service2:1_delete_0 bob.demo" [ style=bold color="green" fontcolor="black"] "stateful:0_post_notify_start_0 alice.demo" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "stateful:0_post_notify_start_0 alice.demo" [ style=bold color="green" fontcolor="black" ] "stateful:0_start_0 alice.demo" -> "ms_stateful_running_0" [ style = bold] "stateful:0_start_0 alice.demo" [ style=bold color="green" fontcolor="black" ] "stateful:1_post_notify_start_0 bob.demo" -> "ms_stateful_confirmed-post_notify_running_0" [ style = bold] "stateful:1_post_notify_start_0 bob.demo" [ style=bold color="green" fontcolor="black" ] "stateful:1_start_0 bob.demo" -> "ms_stateful_running_0" [ style = bold] "stateful:1_start_0 bob.demo" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/bug-lf-2361.exp b/pengine/test10/bug-lf-2361.exp index 60f0d7e828..cba44887a1 100644 --- a/pengine/test10/bug-lf-2361.exp +++ b/pengine/test10/bug-lf-2361.exp @@ -1,138 +1,183 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pengine/test10/bug-lf-2361.summary b/pengine/test10/bug-lf-2361.summary index 338f3e26ec..fd48ba98e3 100644 --- a/pengine/test10/bug-lf-2361.summary +++ b/pengine/test10/bug-lf-2361.summary @@ -1,37 +1,42 @@ Current cluster status: Online: [ alice.demo bob.demo ] dummy1 (ocf::heartbeat:Dummy): Stopped Master/Slave Set: ms_stateful [stateful] Stopped: [ stateful:0 stateful:1 ] Clone Set: cl_dummy2 [dummy2] Stopped: [ dummy2:0 dummy2:1 ] Transition Summary: * Start stateful:0 (alice.demo) * Start stateful:1 (bob.demo) * Start dummy2:0 (alice.demo - blocked) * Start dummy2:1 (bob.demo - blocked) Executing cluster transition: * Pseudo action: ms_stateful_pre_notify_start_0 + * Resource action: service2:0 delete on alice.demo + * Resource action: service2:0 delete on bob.demo + * Resource action: service2:1 delete on bob.demo + * Resource action: service1 delete on alice.demo + * Resource action: service1 delete on bob.demo * Pseudo action: ms_stateful_confirmed-pre_notify_start_0 * Pseudo action: ms_stateful_start_0 * Resource action: stateful:0 start on alice.demo * Resource action: stateful:1 start on bob.demo * Pseudo action: ms_stateful_running_0 * Pseudo action: ms_stateful_post_notify_running_0 * Resource action: stateful:0 notify on alice.demo * Resource action: stateful:1 notify on bob.demo * Pseudo action: ms_stateful_confirmed-post_notify_running_0 Revised cluster status: Online: [ alice.demo bob.demo ] dummy1 (ocf::heartbeat:Dummy): Stopped Master/Slave Set: ms_stateful [stateful] Slaves: [ alice.demo bob.demo ] Clone Set: cl_dummy2 [dummy2] Stopped: [ dummy2:0 dummy2:1 ] diff --git a/pengine/test10/bug-lf-2493.dot b/pengine/test10/bug-lf-2493.dot index b1084c7e16..708eadd71d 100644 --- a/pengine/test10/bug-lf-2493.dot +++ b/pengine/test10/bug-lf-2493.dot @@ -1,7 +1,38 @@ digraph "g" { "p_dummy2_start_0 hpn08" -> "p_dummy4_start_0 hpn07" [ style = bold] "p_dummy2_start_0 hpn08" [ style=bold color="green" fontcolor="black" ] "p_dummy3_start_0 hpn08" -> "p_dummy4_start_0 hpn07" [ style = bold] "p_dummy3_start_0 hpn08" [ style=bold color="green" fontcolor="black" ] "p_dummy4_start_0 hpn07" [ style=bold color="green" fontcolor="black" ] +"res_Filesystem_nfs_fs1_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_nfs_fs1_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_nfs_fs2_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_nfs_fs2_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_nfs_fs3_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_nfs_fs3_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_sap_ci_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_sap_ci_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_sap_db_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_Filesystem_sap_db_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_IPaddr2_nfs_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_IPaddr2_nfs_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_LVM_nfs_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_LVM_nfs_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_LVM_p_vg-sap_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_LVM_p_vg-sap_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_drbd_hpn78:0_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_drbd_hpn78:0_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_drbd_hpn78:1_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_drbd_nfs:0_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_drbd_nfs:0_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_drbd_nfs:1_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_drbd_nfs:1_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_exportfs_fs1_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_exportfs_fs1_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_exportfs_fs2_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_exportfs_fs2_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_exportfs_fs3_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_exportfs_fs3_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] +"res_exportfs_rootfs:0_delete_0 hpn07" [ style=bold color="green" fontcolor="black"] +"res_exportfs_rootfs:1_delete_0 hpn08" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/bug-lf-2493.exp b/pengine/test10/bug-lf-2493.exp index 3d1faa85e1..6c1e267dcb 100644 --- a/pengine/test10/bug-lf-2493.exp +++ b/pengine/test10/bug-lf-2493.exp @@ -1,37 +1,316 @@ - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pengine/test10/bug-lf-2493.summary b/pengine/test10/bug-lf-2493.summary index 874146c889..6b61b1c716 100644 --- a/pengine/test10/bug-lf-2493.summary +++ b/pengine/test10/bug-lf-2493.summary @@ -1,33 +1,64 @@ Current cluster status: Online: [ hpn07 hpn08 ] p_dummy1 (ocf::pacemaker:Dummy): Started hpn07 p_dummy2 (ocf::pacemaker:Dummy): Stopped p_dummy4 (ocf::pacemaker:Dummy): Stopped p_dummy3 (ocf::pacemaker:Dummy): Stopped Master/Slave Set: ms_stateful1 [p_stateful1] Masters: [ hpn07 ] Slaves: [ hpn08 ] Transition Summary: * Start p_dummy2 (hpn08) * Start p_dummy4 (hpn07) * Start p_dummy3 (hpn08) Executing cluster transition: * Resource action: p_dummy2 start on hpn08 * Resource action: p_dummy3 start on hpn08 + * Resource action: res_Filesystem_nfs_fs1 delete on hpn08 + * Resource action: res_Filesystem_nfs_fs1 delete on hpn07 + * Resource action: res_drbd_nfs:0 delete on hpn08 + * Resource action: res_drbd_nfs:0 delete on hpn07 + * Resource action: res_Filesystem_nfs_fs2 delete on hpn08 + * Resource action: res_Filesystem_nfs_fs2 delete on hpn07 + * Resource action: res_Filesystem_nfs_fs3 delete on hpn08 + * Resource action: res_Filesystem_nfs_fs3 delete on hpn07 + * Resource action: res_exportfs_fs1 delete on hpn08 + * Resource action: res_exportfs_fs1 delete on hpn07 + * Resource action: res_exportfs_fs2 delete on hpn08 + * Resource action: res_exportfs_fs2 delete on hpn07 + * Resource action: res_exportfs_fs3 delete on hpn08 + * Resource action: res_exportfs_fs3 delete on hpn07 + * Resource action: res_drbd_nfs:1 delete on hpn08 + * Resource action: res_drbd_nfs:1 delete on hpn07 + * Resource action: res_LVM_nfs delete on hpn08 + * Resource action: res_LVM_nfs delete on hpn07 + * Resource action: res_LVM_p_vg-sap delete on hpn08 + * Resource action: res_LVM_p_vg-sap delete on hpn07 + * Resource action: res_exportfs_rootfs:0 delete on hpn07 + * Resource action: res_IPaddr2_nfs delete on hpn08 + * Resource action: res_IPaddr2_nfs delete on hpn07 + * Resource action: res_drbd_hpn78:0 delete on hpn08 + * Resource action: res_drbd_hpn78:0 delete on hpn07 + * Resource action: res_Filesystem_sap_db delete on hpn08 + * Resource action: res_Filesystem_sap_db delete on hpn07 + * Resource action: res_Filesystem_sap_ci delete on hpn08 + * Resource action: res_Filesystem_sap_ci delete on hpn07 + * Resource action: res_exportfs_rootfs:1 delete on hpn08 + * Resource action: res_drbd_hpn78:1 delete on hpn08 * Resource action: p_dummy4 start on hpn07 Revised cluster status: Online: [ hpn07 hpn08 ] p_dummy1 (ocf::pacemaker:Dummy): Started hpn07 p_dummy2 (ocf::pacemaker:Dummy): Started hpn08 p_dummy4 (ocf::pacemaker:Dummy): Started hpn07 p_dummy3 (ocf::pacemaker:Dummy): Started hpn08 Master/Slave Set: ms_stateful1 [p_stateful1] Masters: [ hpn07 ] Slaves: [ hpn08 ] diff --git a/pengine/test10/clone-anon-dup.dot b/pengine/test10/clone-anon-dup.dot index 85c7fcc6e4..342ad3ac53 100644 --- a/pengine/test10/clone-anon-dup.dot +++ b/pengine/test10/clone-anon-dup.dot @@ -1,31 +1,37 @@ digraph "g" { "all_stopped" [ style=bold color="green" fontcolor="orange" ] +"apache2:0_delete_0 wc01" [ style=bold color="green" fontcolor="black"] +"apache2:0_delete_0 wc02" [ style=bold color="green" fontcolor="black"] +"apache2:0_delete_0 wc03" [ style=bold color="green" fontcolor="black"] "apache2:0_stop_0 wc02" -> "all_stopped" [ style = bold] +"apache2:0_stop_0 wc02" -> "apache2:0_delete_0 wc01" [ style = bold] +"apache2:0_stop_0 wc02" -> "apache2:0_delete_0 wc02" [ style = bold] +"apache2:0_stop_0 wc02" -> "apache2:0_delete_0 wc03" [ style = bold] "apache2:0_stop_0 wc02" -> "group_webservice:2_stopped_0" [ style = bold] "apache2:0_stop_0 wc02" [ style=bold color="green" fontcolor="black"] "clone_webservice_stop_0" -> "clone_webservice_stopped_0" [ style = bold] "clone_webservice_stop_0" -> "group_webservice:2_stop_0" [ style = bold] "clone_webservice_stop_0" [ style=bold color="green" fontcolor="orange" ] "clone_webservice_stopped_0" [ style=bold color="green" fontcolor="orange" ] "group_webservice:2_stop_0" -> "apache2:0_stop_0 wc02" [ style = bold] "group_webservice:2_stop_0" -> "group_webservice:2_stopped_0" [ style = bold] "group_webservice:2_stop_0" [ style=bold color="green" fontcolor="orange" ] "group_webservice:2_stopped_0" -> "clone_webservice_stopped_0" [ style = bold] "group_webservice:2_stopped_0" [ style=bold color="green" fontcolor="orange" ] "probe_complete wc01" -> "probe_complete" [ style = bold] "probe_complete wc01" [ style=bold color="green" fontcolor="black" ] "probe_complete wc02" -> "probe_complete" [ style = bold] "probe_complete wc02" [ style=bold color="green" fontcolor="black" ] "probe_complete wc03" -> "probe_complete" [ style = bold] "probe_complete wc03" [ style=bold color="green" fontcolor="black" ] "probe_complete" -> "apache2:0_stop_0 wc02" [ style = bold] "probe_complete" -> "stonith-1_start_0 wc01" [ style = bold] "probe_complete" [ style=bold color="green" fontcolor="orange" ] "stonith-1_monitor_0 wc01" -> "probe_complete wc01" [ style = bold] "stonith-1_monitor_0 wc01" [ style=bold color="green" fontcolor="black" ] "stonith-1_monitor_0 wc02" -> "probe_complete wc02" [ style = bold] "stonith-1_monitor_0 wc02" [ style=bold color="green" fontcolor="black" ] "stonith-1_monitor_0 wc03" -> "probe_complete wc03" [ style = bold] "stonith-1_monitor_0 wc03" [ style=bold color="green" fontcolor="black" ] "stonith-1_start_0 wc01" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/clone-anon-dup.exp b/pengine/test10/clone-anon-dup.exp index 69ed601c37..e06b55af7e 100644 --- a/pengine/test10/clone-anon-dup.exp +++ b/pengine/test10/clone-anon-dup.exp @@ -1,175 +1,214 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/clone-anon-dup.summary b/pengine/test10/clone-anon-dup.summary index 0bb2f1b7df..ed127c38d3 100644 --- a/pengine/test10/clone-anon-dup.summary +++ b/pengine/test10/clone-anon-dup.summary @@ -1,35 +1,38 @@ Current cluster status: Online: [ wc01 wc02 wc03 ] stonith-1 (stonith:dummy): Stopped Clone Set: clone_webservice [group_webservice] Resource Group: group_webservice:2 fs_www:2 (ocf::heartbeat:Filesystem): ORPHANED Stopped apache2:2 (ocf::heartbeat:apache): ORPHANED Started wc02 Started: [ wc01 wc02 ] Transition Summary: * Start stonith-1 (wc01) * Stop apache2:2 (wc02) Executing cluster transition: * Resource action: stonith-1 monitor on wc03 * Resource action: stonith-1 monitor on wc02 * Resource action: stonith-1 monitor on wc01 * Pseudo action: clone_webservice_stop_0 * Pseudo action: probe_complete * Resource action: stonith-1 start on wc01 * Pseudo action: group_webservice:2_stop_0 * Resource action: apache2:0 stop on wc02 + * Resource action: apache2:0 delete on wc03 + * Resource action: apache2:0 delete on wc01 + * Resource action: apache2:0 delete on wc02 * Pseudo action: all_stopped * Pseudo action: group_webservice:2_stopped_0 * Pseudo action: clone_webservice_stopped_0 Revised cluster status: Online: [ wc01 wc02 wc03 ] stonith-1 (stonith:dummy): Started wc01 Clone Set: clone_webservice [group_webservice] Started: [ wc01 wc02 ] diff --git a/pengine/test10/clone-max-zero.dot b/pengine/test10/clone-max-zero.dot index 33cec0a502..f4d11bdb03 100644 --- a/pengine/test10/clone-max-zero.dot +++ b/pengine/test10/clone-max-zero.dot @@ -1,42 +1,50 @@ digraph "g" { "all_stopped" [ style=bold color="green" fontcolor="orange" ] "c-ocfs2-1_stop_0" -> "c-ocfs2-1_stopped_0" [ style = bold] "c-ocfs2-1_stop_0" -> "ocfs2-1:0_stop_0 c001n11" [ style = bold] "c-ocfs2-1_stop_0" -> "ocfs2-1:1_stop_0 c001n12" [ style = bold] "c-ocfs2-1_stop_0" [ style=bold color="green" fontcolor="orange" ] "c-ocfs2-1_stopped_0" -> "o2cb-clone_stop_0" [ style = bold] "c-ocfs2-1_stopped_0" [ style=bold color="green" fontcolor="orange" ] "dlm-clone_stop_0" -> "dlm-clone_stopped_0" [ style = bold] "dlm-clone_stop_0" -> "dlm:0_stop_0 c001n11" [ style = bold] "dlm-clone_stop_0" -> "dlm:1_stop_0 c001n12" [ style = bold] "dlm-clone_stop_0" [ style=bold color="green" fontcolor="orange" ] "dlm-clone_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"dlm:0_delete_0 c001n11" [ style=bold color="green" fontcolor="black"] +"dlm:0_delete_0 c001n12" [ style=bold color="green" fontcolor="black"] "dlm:0_stop_0 c001n11" -> "all_stopped" [ style = bold] "dlm:0_stop_0 c001n11" -> "dlm-clone_stopped_0" [ style = bold] +"dlm:0_stop_0 c001n11" -> "dlm:0_delete_0 c001n11" [ style = bold] +"dlm:0_stop_0 c001n11" -> "dlm:0_delete_0 c001n12" [ style = bold] "dlm:0_stop_0 c001n11" [ style=bold color="green" fontcolor="black"] +"dlm:1_delete_0 c001n11" [ style=bold color="green" fontcolor="black"] +"dlm:1_delete_0 c001n12" [ style=bold color="green" fontcolor="black"] "dlm:1_stop_0 c001n12" -> "all_stopped" [ style = bold] "dlm:1_stop_0 c001n12" -> "dlm-clone_stopped_0" [ style = bold] +"dlm:1_stop_0 c001n12" -> "dlm:1_delete_0 c001n11" [ style = bold] +"dlm:1_stop_0 c001n12" -> "dlm:1_delete_0 c001n12" [ style = bold] "dlm:1_stop_0 c001n12" [ style=bold color="green" fontcolor="black"] "o2cb-clone_stop_0" -> "o2cb-clone_stopped_0" [ style = bold] "o2cb-clone_stop_0" -> "o2cb:0_stop_0 c001n11" [ style = bold] "o2cb-clone_stop_0" -> "o2cb:1_stop_0 c001n12" [ style = bold] "o2cb-clone_stop_0" [ style=bold color="green" fontcolor="orange" ] "o2cb-clone_stopped_0" -> "dlm-clone_stop_0" [ style = bold] "o2cb-clone_stopped_0" [ style=bold color="green" fontcolor="orange" ] "o2cb:0_stop_0 c001n11" -> "all_stopped" [ style = bold] "o2cb:0_stop_0 c001n11" -> "dlm:0_stop_0 c001n11" [ style = bold] "o2cb:0_stop_0 c001n11" -> "o2cb-clone_stopped_0" [ style = bold] "o2cb:0_stop_0 c001n11" [ style=bold color="green" fontcolor="black" ] "o2cb:1_stop_0 c001n12" -> "all_stopped" [ style = bold] "o2cb:1_stop_0 c001n12" -> "dlm:1_stop_0 c001n12" [ style = bold] "o2cb:1_stop_0 c001n12" -> "o2cb-clone_stopped_0" [ style = bold] "o2cb:1_stop_0 c001n12" [ style=bold color="green" fontcolor="black" ] "ocfs2-1:0_stop_0 c001n11" -> "all_stopped" [ style = bold] "ocfs2-1:0_stop_0 c001n11" -> "c-ocfs2-1_stopped_0" [ style = bold] "ocfs2-1:0_stop_0 c001n11" -> "o2cb:0_stop_0 c001n11" [ style = bold] "ocfs2-1:0_stop_0 c001n11" [ style=bold color="green" fontcolor="black" ] "ocfs2-1:1_stop_0 c001n12" -> "all_stopped" [ style = bold] "ocfs2-1:1_stop_0 c001n12" -> "c-ocfs2-1_stopped_0" [ style = bold] "ocfs2-1:1_stop_0 c001n12" -> "o2cb:1_stop_0 c001n12" [ style = bold] "ocfs2-1:1_stop_0 c001n12" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/clone-max-zero.exp b/pengine/test10/clone-max-zero.exp index d071c4b09b..274c117a9c 100644 --- a/pengine/test10/clone-max-zero.exp +++ b/pengine/test10/clone-max-zero.exp @@ -1,206 +1,258 @@ - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/clone-max-zero.summary b/pengine/test10/clone-max-zero.summary index 0e97f2cb36..2a0b498816 100644 --- a/pengine/test10/clone-max-zero.summary +++ b/pengine/test10/clone-max-zero.summary @@ -1,50 +1,54 @@ Current cluster status: Online: [ c001n11 c001n12 ] fencing (stonith:external/ssh): Started c001n11 Clone Set: dlm-clone [dlm] dlm:0 (ocf::pacemaker:controld): ORPHANED Started c001n12 dlm:1 (ocf::pacemaker:controld): ORPHANED Started c001n11 Clone Set: o2cb-clone [o2cb] Started: [ c001n11 c001n12 ] Clone Set: clone-drbd0 [drbd0] Started: [ c001n11 c001n12 ] Clone Set: c-ocfs2-1 [ocfs2-1] Started: [ c001n11 c001n12 ] Transition Summary: * Stop dlm:0 (c001n12) * Stop dlm:1 (c001n11) * Stop o2cb:0 (c001n12) * Stop o2cb:1 (c001n11) * Stop ocfs2-1:0 (c001n12) * Stop ocfs2-1:1 (c001n11) Executing cluster transition: * Pseudo action: c-ocfs2-1_stop_0 * Resource action: ocfs2-1:1 stop on c001n12 * Resource action: ocfs2-1:0 stop on c001n11 * Pseudo action: c-ocfs2-1_stopped_0 * Pseudo action: o2cb-clone_stop_0 * Resource action: o2cb:1 stop on c001n12 * Resource action: o2cb:0 stop on c001n11 * Pseudo action: o2cb-clone_stopped_0 * Pseudo action: dlm-clone_stop_0 * Resource action: dlm:1 stop on c001n12 + * Resource action: dlm:1 delete on c001n11 + * Resource action: dlm:1 delete on c001n12 * Resource action: dlm:0 stop on c001n11 + * Resource action: dlm:0 delete on c001n11 + * Resource action: dlm:0 delete on c001n12 * Pseudo action: dlm-clone_stopped_0 * Pseudo action: all_stopped Revised cluster status: Online: [ c001n11 c001n12 ] fencing (stonith:external/ssh): Started c001n11 Clone Set: dlm-clone [dlm] Clone Set: o2cb-clone [o2cb] Stopped: [ o2cb:0 o2cb:1 ] Clone Set: clone-drbd0 [drbd0] Started: [ c001n11 c001n12 ] Clone Set: c-ocfs2-1 [ocfs2-1] Stopped: [ ocfs2-1:0 ocfs2-1:1 ] diff --git a/pengine/test10/cloned-group.dot b/pengine/test10/cloned-group.dot index cf6ec27af4..0d71821f82 100644 --- a/pengine/test10/cloned-group.dot +++ b/pengine/test10/cloned-group.dot @@ -1,57 +1,61 @@ digraph "g" { "all_stopped" [ style=bold color="green" fontcolor="orange" ] +"apache2:0_delete_0 webcluster01" [ style=bold color="green" fontcolor="black"] "apache2:0_stop_0 webcluster01" -> "all_stopped" [ style = bold] +"apache2:0_stop_0 webcluster01" -> "apache2:0_delete_0 webcluster01" [ style = bold] "apache2:0_stop_0 webcluster01" -> "grrr:2_stopped_0" [ style = bold] "apache2:0_stop_0 webcluster01" [ style=bold color="green" fontcolor="black"] "apache2:1_monitor_10000 webcluster01" [ style=bold color="green" fontcolor="black" ] "apache2:1_start_0 webcluster01" -> "apache2:1_monitor_10000 webcluster01" [ style = bold] "apache2:1_start_0 webcluster01" -> "grrr:0_running_0" [ style = bold] "apache2:1_start_0 webcluster01" -> "mysql-proxy:1_start_0 webcluster01" [ style = bold] "apache2:1_start_0 webcluster01" [ style=bold color="green" fontcolor="black" ] "apache2:1_stop_0 webcluster01" -> "all_stopped" [ style = bold] "apache2:1_stop_0 webcluster01" -> "apache2:1_start_0 webcluster01" [ style = bold] "apache2:1_stop_0 webcluster01" -> "grrr:0_stopped_0" [ style = bold] "apache2:1_stop_0 webcluster01" [ style=bold color="green" fontcolor="black" ] "apache2_clone_running_0" [ style=bold color="green" fontcolor="orange" ] "apache2_clone_start_0" -> "apache2_clone_running_0" [ style = bold] "apache2_clone_start_0" -> "grrr:0_start_0" [ style = bold] "apache2_clone_start_0" [ style=bold color="green" fontcolor="orange" ] "apache2_clone_stop_0" -> "apache2_clone_stopped_0" [ style = bold] "apache2_clone_stop_0" -> "grrr:0_stop_0" [ style = bold] "apache2_clone_stop_0" -> "grrr:2_stop_0" [ style = bold] "apache2_clone_stop_0" [ style=bold color="green" fontcolor="orange" ] "apache2_clone_stopped_0" -> "apache2_clone_start_0" [ style = bold] "apache2_clone_stopped_0" [ style=bold color="green" fontcolor="orange" ] "grrr:0_running_0" -> "apache2_clone_running_0" [ style = bold] "grrr:0_running_0" [ style=bold color="green" fontcolor="orange"] "grrr:0_start_0" -> "apache2:1_start_0 webcluster01" [ style = bold] "grrr:0_start_0" -> "grrr:0_running_0" [ style = bold] "grrr:0_start_0" -> "mysql-proxy:1_start_0 webcluster01" [ style = bold] "grrr:0_start_0" [ style=bold color="green" fontcolor="orange"] "grrr:0_stop_0" -> "apache2:1_stop_0 webcluster01" [ style = bold] "grrr:0_stop_0" -> "grrr:0_stopped_0" [ style = bold] "grrr:0_stop_0" -> "mysql-proxy:1_stop_0 webcluster01" [ style = bold] "grrr:0_stop_0" [ style=bold color="green" fontcolor="orange"] "grrr:0_stopped_0" -> "apache2_clone_stopped_0" [ style = bold] "grrr:0_stopped_0" -> "grrr:0_start_0" [ style = bold] "grrr:0_stopped_0" [ style=bold color="green" fontcolor="orange"] "grrr:2_stop_0" -> "apache2:0_stop_0 webcluster01" [ style = bold] "grrr:2_stop_0" -> "grrr:2_stopped_0" [ style = bold] "grrr:2_stop_0" -> "mysql-proxy:0_stop_0 webcluster01" [ style = bold] "grrr:2_stop_0" [ style=bold color="green" fontcolor="orange" ] "grrr:2_stopped_0" -> "apache2_clone_stopped_0" [ style = bold] "grrr:2_stopped_0" [ style=bold color="green" fontcolor="orange" ] +"mysql-proxy:0_delete_0 webcluster01" [ style=bold color="green" fontcolor="black"] "mysql-proxy:0_stop_0 webcluster01" -> "all_stopped" [ style = bold] "mysql-proxy:0_stop_0 webcluster01" -> "apache2:0_stop_0 webcluster01" [ style = bold] "mysql-proxy:0_stop_0 webcluster01" -> "grrr:2_stopped_0" [ style = bold] +"mysql-proxy:0_stop_0 webcluster01" -> "mysql-proxy:0_delete_0 webcluster01" [ style = bold] "mysql-proxy:0_stop_0 webcluster01" [ style=bold color="green" fontcolor="black"] "mysql-proxy:1_monitor_10000 webcluster01" [ style=bold color="green" fontcolor="black" ] "mysql-proxy:1_start_0 webcluster01" -> "grrr:0_running_0" [ style = bold] "mysql-proxy:1_start_0 webcluster01" -> "mysql-proxy:1_monitor_10000 webcluster01" [ style = bold] "mysql-proxy:1_start_0 webcluster01" [ style=bold color="green" fontcolor="black" ] "mysql-proxy:1_stop_0 webcluster01" -> "all_stopped" [ style = bold] "mysql-proxy:1_stop_0 webcluster01" -> "apache2:1_stop_0 webcluster01" [ style = bold] "mysql-proxy:1_stop_0 webcluster01" -> "grrr:0_stopped_0" [ style = bold] "mysql-proxy:1_stop_0 webcluster01" -> "mysql-proxy:1_start_0 webcluster01" [ style = bold] "mysql-proxy:1_stop_0 webcluster01" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/cloned-group.exp b/pengine/test10/cloned-group.exp index 102bdddfcb..0a833b8884 100644 --- a/pengine/test10/cloned-group.exp +++ b/pengine/test10/cloned-group.exp @@ -1,289 +1,315 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/cloned-group.summary b/pengine/test10/cloned-group.summary index 846f315e2c..1ec8e79641 100644 --- a/pengine/test10/cloned-group.summary +++ b/pengine/test10/cloned-group.summary @@ -1,47 +1,49 @@ Current cluster status: Online: [ webcluster01 ] OFFLINE: [ webcluster02 ] Clone Set: apache2_clone [grrr] Resource Group: grrr:2 apache2:2 (ocf::heartbeat:apache): ORPHANED Started webcluster01 mysql-proxy:2 (lsb:mysql-proxy): ORPHANED Started webcluster01 Started: [ webcluster01 ] Stopped: [ grrr:1 ] Transition Summary: * Restart apache2:0 (Started webcluster01) * Restart mysql-proxy:0 (Started webcluster01) * Stop apache2:2 (webcluster01) * Stop mysql-proxy:2 (webcluster01) Executing cluster transition: * Pseudo action: apache2_clone_stop_0 * Pseudo action: grrr:0_stop_0 * Resource action: mysql-proxy:1 stop on webcluster01 * Pseudo action: grrr:2_stop_0 * Resource action: mysql-proxy:0 stop on webcluster01 + * Resource action: mysql-proxy:0 delete on webcluster01 * Resource action: apache2:1 stop on webcluster01 * Resource action: apache2:0 stop on webcluster01 + * Resource action: apache2:0 delete on webcluster01 * Pseudo action: all_stopped * Pseudo action: grrr:0_stopped_0 * Pseudo action: grrr:2_stopped_0 * Pseudo action: apache2_clone_stopped_0 * Pseudo action: apache2_clone_start_0 * Pseudo action: grrr:0_start_0 * Resource action: apache2:1 start on webcluster01 * Resource action: apache2:1 monitor=10000 on webcluster01 * Resource action: mysql-proxy:1 start on webcluster01 * Resource action: mysql-proxy:1 monitor=10000 on webcluster01 * Pseudo action: grrr:0_running_0 * Pseudo action: apache2_clone_running_0 Revised cluster status: Online: [ webcluster01 ] OFFLINE: [ webcluster02 ] Clone Set: apache2_clone [grrr] Started: [ webcluster01 ] Stopped: [ grrr:1 ] diff --git a/pengine/test10/coloc-intra-set.dot b/pengine/test10/coloc-intra-set.dot index 0b32ad021d..a5ddb3ba8f 100644 --- a/pengine/test10/coloc-intra-set.dot +++ b/pengine/test10/coloc-intra-set.dot @@ -1,15 +1,20 @@ digraph "g" { "all_stopped" [ style=bold color="green" fontcolor="orange" ] +"d0:0_delete_0 hex-13" [ style=bold color="green" fontcolor="black"] +"dlm:0_delete_0 hex-13" [ style=bold color="green" fontcolor="black"] "dummy1_monitor_15000 hex-14" [ style=bold color="green" fontcolor="black" ] "dummy1_start_0 hex-14" -> "dummy1_monitor_15000 hex-14" [ style = bold] "dummy1_start_0 hex-14" [ style=bold color="green" fontcolor="black" ] "dummy1_stop_0 hex-13" -> "all_stopped" [ style = bold] "dummy1_stop_0 hex-13" -> "dummy1_start_0 hex-14" [ style = bold] "dummy1_stop_0 hex-13" [ style=bold color="green" fontcolor="black" ] "dummy3_monitor_15000 hex-14" [ style=bold color="green" fontcolor="black" ] "dummy3_start_0 hex-14" -> "dummy3_monitor_15000 hex-14" [ style = bold] "dummy3_start_0 hex-14" [ style=bold color="green" fontcolor="black" ] "dummy3_stop_0 hex-13" -> "all_stopped" [ style = bold] "dummy3_stop_0 hex-13" -> "dummy3_start_0 hex-14" [ style = bold] "dummy3_stop_0 hex-13" [ style=bold color="green" fontcolor="black" ] +"dummy4_delete_0 hex-13" [ style=bold color="green" fontcolor="black"] +"o2cb:0_delete_0 hex-13" [ style=bold color="green" fontcolor="black"] +"ocfs2-3:0_delete_0 hex-13" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/coloc-intra-set.exp b/pengine/test10/coloc-intra-set.exp index 99bb815304..29622d4a03 100644 --- a/pengine/test10/coloc-intra-set.exp +++ b/pengine/test10/coloc-intra-set.exp @@ -1,88 +1,133 @@ - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + diff --git a/pengine/test10/coloc-intra-set.summary b/pengine/test10/coloc-intra-set.summary index 4570ab2b51..caefd3e2ef 100644 --- a/pengine/test10/coloc-intra-set.summary +++ b/pengine/test10/coloc-intra-set.summary @@ -1,32 +1,37 @@ Current cluster status: Online: [ hex-13 hex-14 ] fencing-sbd (stonith:external/sbd): Started hex-13 dummy0 (ocf::heartbeat:Dummy): Started hex-14 dummy1 (ocf::heartbeat:Dummy): Started hex-13 dummy2 (ocf::heartbeat:Dummy): Started hex-14 dummy3 (ocf::heartbeat:Dummy): Started hex-13 Transition Summary: * Move dummy1 (Started hex-13 -> hex-14) * Move dummy3 (Started hex-13 -> hex-14) Executing cluster transition: * Resource action: dummy1 stop on hex-13 * Resource action: dummy3 stop on hex-13 + * Resource action: d0:0 delete on hex-13 + * Resource action: o2cb:0 delete on hex-13 + * Resource action: dummy4 delete on hex-13 + * Resource action: dlm:0 delete on hex-13 + * Resource action: ocfs2-3:0 delete on hex-13 * Pseudo action: all_stopped * Resource action: dummy1 start on hex-14 * Resource action: dummy3 start on hex-14 * Resource action: dummy1 monitor=15000 on hex-14 * Resource action: dummy3 monitor=15000 on hex-14 Revised cluster status: Online: [ hex-13 hex-14 ] fencing-sbd (stonith:external/sbd): Started hex-13 dummy0 (ocf::heartbeat:Dummy): Started hex-14 dummy1 (ocf::heartbeat:Dummy): Started hex-14 dummy2 (ocf::heartbeat:Dummy): Started hex-14 dummy3 (ocf::heartbeat:Dummy): Started hex-14 diff --git a/pengine/test10/failcount.dot b/pengine/test10/failcount.dot index ed3c165ce9..aa7124efc9 100644 --- a/pengine/test10/failcount.dot +++ b/pengine/test10/failcount.dot @@ -1,4 +1,22 @@ digraph "g" { +"re-dummy1_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-dummy1_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-dummy2_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-dummy2_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-dummy3_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-dummy3_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-dummy4_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-dummy4_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-dummy_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-dummy_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-monitoring-lv:0_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-monitoring-lv:0_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-monitoring-lv_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-monitoring-lv_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-named-103ns1-ip_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-named-103ns1-ip_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] +"re-named-103ns2-ip_delete_0 dresproddns01" [ style=bold color="green" fontcolor="black"] +"re-named-103ns2-ip_delete_0 dresproddns02" [ style=bold color="green" fontcolor="black"] "re-openfire-lsb:0_clear_failcount dresproddns01" [ style=bold color="green" fontcolor="black"] "re-openfire-lsb:1_clear_failcount dresproddns02" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/failcount.exp b/pengine/test10/failcount.exp index 3b2057e7aa..396bdb14ef 100644 --- a/pengine/test10/failcount.exp +++ b/pengine/test10/failcount.exp @@ -1,21 +1,183 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/pengine/test10/failcount.summary b/pengine/test10/failcount.summary index 9d60f46062..9ee764fd95 100644 --- a/pengine/test10/failcount.summary +++ b/pengine/test10/failcount.summary @@ -1,59 +1,77 @@ Current cluster status: Online: [ dresproddns01 dresproddns02 ] Clone Set: cl-openfire [gr-openfire] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-named [gr-named] Started: [ dresproddns01 dresproddns02 ] re-monitor (ocf::pacemaker:ClusterMon): Started dresproddns02 Clone Set: cl-ping-sjim [re-ping-sjim] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-wesbprod [gr-wesbprod] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-svn [gr-svn] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-ldirectord [re-ldirectord-gluster] Started: [ dresproddns01 dresproddns02 ] re-drdns-ip (ocf::heartbeat:IPaddr): Started dresproddns01 Clone Set: cl-cdeprod [gr-cdeprod] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-sysinfo [re-sysinfo] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-haproxy [gr-haproxy] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-ping-sjho [re-ping-sjho] Started: [ dresproddns01 dresproddns02 ] Transition Summary: Executing cluster transition: * Cluster action: clear_failcount on dresproddns01 * Cluster action: clear_failcount on dresproddns02 + * Resource action: re-named-103ns2-ip delete on dresproddns02 + * Resource action: re-named-103ns2-ip delete on dresproddns01 + * Resource action: re-monitoring-lv:0 delete on dresproddns02 + * Resource action: re-monitoring-lv:0 delete on dresproddns01 + * Resource action: re-dummy delete on dresproddns02 + * Resource action: re-dummy delete on dresproddns01 + * Resource action: re-dummy3 delete on dresproddns02 + * Resource action: re-dummy3 delete on dresproddns01 + * Resource action: re-dummy4 delete on dresproddns02 + * Resource action: re-dummy4 delete on dresproddns01 + * Resource action: re-dummy1 delete on dresproddns02 + * Resource action: re-dummy1 delete on dresproddns01 + * Resource action: re-monitoring-lv delete on dresproddns02 + * Resource action: re-monitoring-lv delete on dresproddns01 + * Resource action: re-dummy2 delete on dresproddns02 + * Resource action: re-dummy2 delete on dresproddns01 + * Resource action: re-named-103ns1-ip delete on dresproddns02 + * Resource action: re-named-103ns1-ip delete on dresproddns01 Revised cluster status: Online: [ dresproddns01 dresproddns02 ] Clone Set: cl-openfire [gr-openfire] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-named [gr-named] Started: [ dresproddns01 dresproddns02 ] re-monitor (ocf::pacemaker:ClusterMon): Started dresproddns02 Clone Set: cl-ping-sjim [re-ping-sjim] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-wesbprod [gr-wesbprod] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-svn [gr-svn] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-ldirectord [re-ldirectord-gluster] Started: [ dresproddns01 dresproddns02 ] re-drdns-ip (ocf::heartbeat:IPaddr): Started dresproddns01 Clone Set: cl-cdeprod [gr-cdeprod] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-sysinfo [re-sysinfo] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-haproxy [gr-haproxy] Started: [ dresproddns01 dresproddns02 ] Clone Set: cl-ping-sjho [re-ping-sjho] Started: [ dresproddns01 dresproddns02 ] diff --git a/pengine/test10/inc9.dot b/pengine/test10/inc9.dot index f4d6649003..8e297386b8 100644 --- a/pengine/test10/inc9.dot +++ b/pengine/test10/inc9.dot @@ -1,18 +1,26 @@ digraph "g" { "all_stopped" [ style=bold color="green" fontcolor="orange" ] +"child_rsc1:1_delete_0 node1" [ style=bold color="green" fontcolor="black"] +"child_rsc1:1_delete_0 node2" [ style=bold color="green" fontcolor="black"] "child_rsc1:1_stop_0 node1" -> "all_stopped" [ style = bold] +"child_rsc1:1_stop_0 node1" -> "child_rsc1:1_delete_0 node1" [ style = bold] +"child_rsc1:1_stop_0 node1" -> "child_rsc1:1_delete_0 node2" [ style = bold] "child_rsc1:1_stop_0 node1" -> "rsc1_stopped_0" [ style = bold] "child_rsc1:1_stop_0 node1" [ style=bold color="green" fontcolor="black"] "child_rsc1:1_stop_0 node2" -> "all_stopped" [ style = bold] +"child_rsc1:1_stop_0 node2" -> "child_rsc1:1_delete_0 node1" [ style = bold] +"child_rsc1:1_stop_0 node2" -> "child_rsc1:1_delete_0 node2" [ style = bold] "child_rsc1:1_stop_0 node2" -> "rsc1_stopped_0" [ style = bold] "child_rsc1:1_stop_0 node2" [ style=bold color="green" fontcolor="black"] +"child_rsc1:2_delete_0 node1" [ style=bold color="green" fontcolor="black"] "child_rsc1:2_stop_0 node1" -> "all_stopped" [ style = bold] +"child_rsc1:2_stop_0 node1" -> "child_rsc1:2_delete_0 node1" [ style = bold] "child_rsc1:2_stop_0 node1" -> "rsc1_stopped_0" [ style = bold] "child_rsc1:2_stop_0 node1" [ style=bold color="green" fontcolor="black"] "rsc1_stop_0" -> "child_rsc1:1_stop_0 node1" [ style = bold] "rsc1_stop_0" -> "child_rsc1:1_stop_0 node2" [ style = bold] "rsc1_stop_0" -> "child_rsc1:2_stop_0 node1" [ style = bold] "rsc1_stop_0" -> "rsc1_stopped_0" [ style = bold] "rsc1_stop_0" [ style=bold color="green" fontcolor="orange" ] "rsc1_stopped_0" [ style=bold color="green" fontcolor="orange" ] } diff --git a/pengine/test10/inc9.exp b/pengine/test10/inc9.exp index e8ffd91eeb..8610824f43 100644 --- a/pengine/test10/inc9.exp +++ b/pengine/test10/inc9.exp @@ -1,89 +1,154 @@ - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/inc9.summary b/pengine/test10/inc9.summary index 7425549105..f7ae82adfb 100644 --- a/pengine/test10/inc9.summary +++ b/pengine/test10/inc9.summary @@ -1,31 +1,36 @@ Current cluster status: Online: [ node1 node2 ] Clone Set: rsc1 [child_rsc1] child_rsc1:5 (heartbeat:apache): ORPHANED Started node1 child_rsc1:6 (heartbeat:apache): ORPHANED Started node1 child_rsc1:7 (heartbeat:apache): ORPHANED Started node2 Started: [ node1 node2 ] Stopped: [ child_rsc1:2 child_rsc1:3 child_rsc1:4 ] Transition Summary: * Stop child_rsc1:5 (node1) * Stop child_rsc1:6 (node1) * Stop child_rsc1:7 (node2) Executing cluster transition: * Pseudo action: rsc1_stop_0 * Resource action: child_rsc1:1 stop on node1 + * Resource action: child_rsc1:1 delete on node2 + * Resource action: child_rsc1:1 delete on node1 * Resource action: child_rsc1:2 stop on node1 + * Resource action: child_rsc1:2 delete on node1 * Resource action: child_rsc1:1 stop on node2 + * Resource action: child_rsc1:1 delete on node2 + * Resource action: child_rsc1:1 delete on node1 * Pseudo action: rsc1_stopped_0 * Pseudo action: all_stopped Revised cluster status: Online: [ node1 node2 ] Clone Set: rsc1 [child_rsc1] Started: [ node1 node2 ] Stopped: [ child_rsc1:2 child_rsc1:3 child_rsc1:4 ] diff --git a/pengine/test10/obsolete-lrm-resource.dot b/pengine/test10/obsolete-lrm-resource.dot index 85b12efc8c..b42feb9965 100644 --- a/pengine/test10/obsolete-lrm-resource.dot +++ b/pengine/test10/obsolete-lrm-resource.dot @@ -1,14 +1,15 @@ digraph "g" { "probe_complete yingying.site" -> "probe_complete" [ style = bold] "probe_complete yingying.site" [ style=bold color="green" fontcolor="black"] "probe_complete" -> "rsc1_child:0_start_0 yingying.site" [ style = bold] "probe_complete" [ style=bold color="green" fontcolor="orange"] "rsc1_child:0_monitor_0 yingying.site" -> "probe_complete yingying.site" [ style = bold] "rsc1_child:0_monitor_0 yingying.site" [ style=bold color="green" fontcolor="black"] "rsc1_child:0_start_0 yingying.site" -> "rsc1_running_0" [ style = bold] "rsc1_child:0_start_0 yingying.site" [ style=bold color="green" fontcolor="black"] +"rsc1_delete_0 yingying.site" [ style=bold color="green" fontcolor="black"] "rsc1_running_0" [ style=bold color="green" fontcolor="orange"] "rsc1_start_0" -> "rsc1_child:0_start_0 yingying.site" [ style = bold] "rsc1_start_0" -> "rsc1_running_0" [ style = bold] "rsc1_start_0" [ style=bold color="green" fontcolor="orange"] } diff --git a/pengine/test10/obsolete-lrm-resource.exp b/pengine/test10/obsolete-lrm-resource.exp index b7b6ce7f8c..e56e556995 100644 --- a/pengine/test10/obsolete-lrm-resource.exp +++ b/pengine/test10/obsolete-lrm-resource.exp @@ -1,75 +1,84 @@ - + - + - + - + - + - + - + - + - + - + + + + + + + + + + - + - + - + - + diff --git a/pengine/test10/obsolete-lrm-resource.summary b/pengine/test10/obsolete-lrm-resource.summary index d89195386e..f45bdd5f45 100644 --- a/pengine/test10/obsolete-lrm-resource.summary +++ b/pengine/test10/obsolete-lrm-resource.summary @@ -1,23 +1,24 @@ Current cluster status: Online: [ yingying.site ] Clone Set: rsc1 [rsc1_child] Stopped: [ rsc1_child:0 ] Transition Summary: * Start rsc1_child:0 (yingying.site) Executing cluster transition: * Resource action: rsc1_child:0 monitor on yingying.site * Pseudo action: rsc1_start_0 + * Resource action: rsc1 delete on yingying.site * Pseudo action: probe_complete * Resource action: rsc1_child:0 start on yingying.site * Pseudo action: rsc1_running_0 Revised cluster status: Online: [ yingying.site ] Clone Set: rsc1 [rsc1_child] Started: [ yingying.site ] diff --git a/pengine/test10/orphan-0.exp b/pengine/test10/orphan-0.exp index c9f649de15..afccffbb46 100644 --- a/pengine/test10/orphan-0.exp +++ b/pengine/test10/orphan-0.exp @@ -1,213 +1,213 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/orphan-1.dot b/pengine/test10/orphan-1.dot index 511dd8b4cb..8710cdcad7 100644 --- a/pengine/test10/orphan-1.dot +++ b/pengine/test10/orphan-1.dot @@ -1,42 +1,44 @@ digraph "g" { "Cancel rsc_c001n02_monitor_5000 c001n02" [ style=bold color="green" fontcolor="black" ] "Cancel rsc_c001n03_monitor_5000 c001n03" [ style=bold color="green" fontcolor="black" ] "DcIPaddr_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "DcIPaddr_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "DcIPaddr_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "DcIPaddr_monitor_0 c001n03" [ style=bold color="green" fontcolor="black" ] "DcIPaddr_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "DcIPaddr_monitor_0 c001n08" [ style=bold color="green" fontcolor="black" ] "all_stopped" [ style=bold color="green" fontcolor="orange" ] "probe_complete c001n01" -> "probe_complete" [ style = bold] "probe_complete c001n01" [ style=bold color="green" fontcolor="black" ] "probe_complete c001n02" -> "probe_complete" [ style = bold] "probe_complete c001n02" [ style=bold color="green" fontcolor="black" ] "probe_complete c001n03" -> "probe_complete" [ style = bold] "probe_complete c001n03" [ style=bold color="green" fontcolor="black" ] "probe_complete c001n08" -> "probe_complete" [ style = bold] "probe_complete c001n08" [ style=bold color="green" fontcolor="black" ] "probe_complete" -> "rsc_c001n08_stop_0 c001n08" [ style = bold] "probe_complete" [ style=bold color="green" fontcolor="orange" ] "rsc_c001n01_monitor_0 c001n02" -> "probe_complete c001n02" [ style = bold] "rsc_c001n01_monitor_0 c001n02" [ style=bold color="green" fontcolor="black" ] "rsc_c001n01_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "rsc_c001n01_monitor_0 c001n03" [ style=bold color="green" fontcolor="black" ] "rsc_c001n01_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "rsc_c001n01_monitor_0 c001n08" [ style=bold color="green" fontcolor="black" ] "rsc_c001n02_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "rsc_c001n02_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "rsc_c001n02_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "rsc_c001n02_monitor_0 c001n03" [ style=bold color="green" fontcolor="black" ] "rsc_c001n02_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "rsc_c001n02_monitor_0 c001n08" [ style=bold color="green" fontcolor="black" ] "rsc_c001n03_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "rsc_c001n03_monitor_0 c001n01" [ style=bold color="green" fontcolor="black" ] "rsc_c001n03_monitor_0 c001n02" -> "probe_complete c001n02" [ style = bold] "rsc_c001n03_monitor_0 c001n02" [ style=bold color="green" fontcolor="black" ] "rsc_c001n03_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "rsc_c001n03_monitor_0 c001n08" [ style=bold color="green" fontcolor="black" ] "rsc_c001n03_monitor_6000 c001n03" [ style=bold color="green" fontcolor="black" ] +"rsc_c001n08_delete_0 c001n08" [ style=bold color="green" fontcolor="black"] "rsc_c001n08_stop_0 c001n08" -> "all_stopped" [ style = bold] +"rsc_c001n08_stop_0 c001n08" -> "rsc_c001n08_delete_0 c001n08" [ style = bold] "rsc_c001n08_stop_0 c001n08" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/orphan-1.exp b/pengine/test10/orphan-1.exp index 8618b18d07..b3a647065b 100644 --- a/pengine/test10/orphan-1.exp +++ b/pengine/test10/orphan-1.exp @@ -1,256 +1,269 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/orphan-1.summary b/pengine/test10/orphan-1.summary index bc07c42373..a669ea8688 100644 --- a/pengine/test10/orphan-1.summary +++ b/pengine/test10/orphan-1.summary @@ -1,41 +1,42 @@ Current cluster status: Online: [ c001n01 c001n02 c001n03 c001n08 ] DcIPaddr (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n02 (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n03 (ocf::heartbeat:IPaddr): Started c001n03 rsc_c001n01 (ocf::heartbeat:IPaddr): Started c001n01 rsc_c001n08 (ocf::heartbeat:IPaddr): ORPHANED Started c001n08 Transition Summary: * Stop rsc_c001n08 (c001n08) Executing cluster transition: * Resource action: DcIPaddr monitor on c001n08 * Resource action: DcIPaddr monitor on c001n03 * Resource action: DcIPaddr monitor on c001n01 * Resource action: rsc_c001n02 monitor on c001n08 * Resource action: rsc_c001n02 monitor on c001n03 * Resource action: rsc_c001n02 monitor on c001n01 * Resource action: rsc_c001n02 cancel=5000 on c001n02 * Resource action: rsc_c001n03 monitor=6000 on c001n03 * Resource action: rsc_c001n03 monitor on c001n08 * Resource action: rsc_c001n03 monitor on c001n02 * Resource action: rsc_c001n03 monitor on c001n01 * Resource action: rsc_c001n03 cancel=5000 on c001n03 * Resource action: rsc_c001n01 monitor on c001n08 * Resource action: rsc_c001n01 monitor on c001n03 * Resource action: rsc_c001n01 monitor on c001n02 * Pseudo action: probe_complete * Resource action: rsc_c001n08 stop on c001n08 + * Resource action: rsc_c001n08 delete on c001n08 * Pseudo action: all_stopped Revised cluster status: Online: [ c001n01 c001n02 c001n03 c001n08 ] DcIPaddr (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n02 (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n03 (ocf::heartbeat:IPaddr): Started c001n03 rsc_c001n01 (ocf::heartbeat:IPaddr): Started c001n01 diff --git a/pengine/test10/orphan-2.dot b/pengine/test10/orphan-2.dot index 4037b90b6f..09fb0bdc0d 100644 --- a/pengine/test10/orphan-2.dot +++ b/pengine/test10/orphan-2.dot @@ -1,46 +1,48 @@ digraph "g" { "Cancel rsc_c001n02_monitor_5000 c001n02" [ style=bold color="green" fontcolor="black"] "Cancel rsc_c001n03_monitor_5000 c001n03" [ style=bold color="green" fontcolor="black"] "DcIPaddr_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "DcIPaddr_monitor_0 c001n01" [ style=bold color="green" fontcolor="black"] "DcIPaddr_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "DcIPaddr_monitor_0 c001n03" [ style=bold color="green" fontcolor="black"] "DcIPaddr_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "DcIPaddr_monitor_0 c001n08" [ style=bold color="green" fontcolor="black"] "all_stopped" [ style=bold color="green" fontcolor="orange"] "probe_complete c001n01" -> "probe_complete" [ style = bold] "probe_complete c001n01" [ style=bold color="green" fontcolor="black"] "probe_complete c001n02" -> "probe_complete" [ style = bold] "probe_complete c001n02" [ style=bold color="green" fontcolor="black"] "probe_complete c001n03" -> "probe_complete" [ style = bold] "probe_complete c001n03" [ style=bold color="green" fontcolor="black"] "probe_complete c001n08" -> "probe_complete" [ style = bold] "probe_complete c001n08" [ style=bold color="green" fontcolor="black"] "probe_complete" -> "rsc_c001n08_stop_0 c001n08" [ style = bold] "probe_complete" [ style=bold color="green" fontcolor="orange"] "rsc_c001n01_monitor_0 c001n02" -> "probe_complete c001n02" [ style = bold] "rsc_c001n01_monitor_0 c001n02" [ style=bold color="green" fontcolor="black"] "rsc_c001n01_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "rsc_c001n01_monitor_0 c001n03" [ style=bold color="green" fontcolor="black"] "rsc_c001n01_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "rsc_c001n01_monitor_0 c001n08" [ style=bold color="green" fontcolor="black"] "rsc_c001n02_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "rsc_c001n02_monitor_0 c001n01" [ style=bold color="green" fontcolor="black"] "rsc_c001n02_monitor_0 c001n03" -> "probe_complete c001n03" [ style = bold] "rsc_c001n02_monitor_0 c001n03" [ style=bold color="green" fontcolor="black"] "rsc_c001n02_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "rsc_c001n02_monitor_0 c001n08" [ style=bold color="green" fontcolor="black"] "rsc_c001n03_monitor_0 c001n01" -> "probe_complete c001n01" [ style = bold] "rsc_c001n03_monitor_0 c001n01" [ style=bold color="green" fontcolor="black"] "rsc_c001n03_monitor_0 c001n02" -> "probe_complete c001n02" [ style = bold] "rsc_c001n03_monitor_0 c001n02" [ style=bold color="green" fontcolor="black"] "rsc_c001n03_monitor_0 c001n08" -> "probe_complete c001n08" [ style = bold] "rsc_c001n03_monitor_0 c001n08" [ style=bold color="green" fontcolor="black"] "rsc_c001n03_monitor_6000 c001n03" [ style=bold color="green" fontcolor="black"] "rsc_c001n08_clear_failcount c001n02" -> "probe_complete" [ style = bold] "rsc_c001n08_clear_failcount c001n02" [ style=bold color="green" fontcolor="black"] "rsc_c001n08_clear_failcount c001n08" -> "probe_complete" [ style = bold] "rsc_c001n08_clear_failcount c001n08" [ style=bold color="green" fontcolor="black"] +"rsc_c001n08_delete_0 c001n08" [ style=bold color="green" fontcolor="black"] "rsc_c001n08_stop_0 c001n08" -> "all_stopped" [ style = bold] +"rsc_c001n08_stop_0 c001n08" -> "rsc_c001n08_delete_0 c001n08" [ style = bold] "rsc_c001n08_stop_0 c001n08" [ style=bold color="green" fontcolor="black"] } diff --git a/pengine/test10/orphan-2.exp b/pengine/test10/orphan-2.exp index bc0e42aa26..ad9ad5984d 100644 --- a/pengine/test10/orphan-2.exp +++ b/pengine/test10/orphan-2.exp @@ -1,280 +1,293 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + diff --git a/pengine/test10/orphan-2.summary b/pengine/test10/orphan-2.summary index 8ce2fc3ec7..e20bb18b30 100644 --- a/pengine/test10/orphan-2.summary +++ b/pengine/test10/orphan-2.summary @@ -1,43 +1,44 @@ Current cluster status: Online: [ c001n01 c001n02 c001n03 c001n08 ] DcIPaddr (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n02 (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n03 (ocf::heartbeat:IPaddr): Started c001n03 rsc_c001n01 (ocf::heartbeat:IPaddr): Started c001n01 rsc_c001n08 (ocf::heartbeat:IPaddr): ORPHANED Started c001n08 Transition Summary: * Stop rsc_c001n08 (c001n08) Executing cluster transition: * Resource action: DcIPaddr monitor on c001n08 * Resource action: DcIPaddr monitor on c001n03 * Resource action: DcIPaddr monitor on c001n01 * Resource action: rsc_c001n02 monitor on c001n08 * Resource action: rsc_c001n02 monitor on c001n03 * Resource action: rsc_c001n02 monitor on c001n01 * Resource action: rsc_c001n02 cancel=5000 on c001n02 * Resource action: rsc_c001n03 monitor=6000 on c001n03 * Resource action: rsc_c001n03 monitor on c001n08 * Resource action: rsc_c001n03 monitor on c001n02 * Resource action: rsc_c001n03 monitor on c001n01 * Resource action: rsc_c001n03 cancel=5000 on c001n03 * Resource action: rsc_c001n01 monitor on c001n08 * Resource action: rsc_c001n01 monitor on c001n03 * Resource action: rsc_c001n01 monitor on c001n02 * Cluster action: clear_failcount on c001n08 * Cluster action: clear_failcount on c001n02 * Pseudo action: probe_complete * Resource action: rsc_c001n08 stop on c001n08 + * Resource action: rsc_c001n08 delete on c001n08 * Pseudo action: all_stopped Revised cluster status: Online: [ c001n01 c001n02 c001n03 c001n08 ] DcIPaddr (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n02 (ocf::heartbeat:IPaddr): Started c001n02 rsc_c001n03 (ocf::heartbeat:IPaddr): Started c001n03 rsc_c001n01 (ocf::heartbeat:IPaddr): Started c001n01 diff --git a/pengine/test10/params-2.dot b/pengine/test10/params-2.dot index 5778c67d9f..82aa24fd41 100644 --- a/pengine/test10/params-2.dot +++ b/pengine/test10/params-2.dot @@ -1,39 +1,37 @@ digraph "g" { "all_stopped" [ style=bold color="green" fontcolor="orange" ] "do_shutdown node1" [ style=bold color="green" fontcolor="black" ] -"lrm_refresh node2" [ style=bold color="green" fontcolor="black" ] "probe_complete node1" -> "probe_complete" [ style = bold] "probe_complete node1" [ style=bold color="green" fontcolor="black" ] "probe_complete node2" -> "probe_complete" [ style = bold] "probe_complete node2" [ style=bold color="green" fontcolor="black" ] "probe_complete node3" -> "probe_complete" [ style = bold] "probe_complete node3" [ style=bold color="green" fontcolor="black" ] "probe_complete" -> "rsc2_start_0 node2" [ style = bold] "probe_complete" -> "rsc2_stop_0 node2" [ style = bold] "probe_complete" -> "rsc3_start_0 node3" [ style = bold] "probe_complete" [ style=bold color="green" fontcolor="orange" ] "rsc1_monitor_0 node2" -> "probe_complete node2" [ style = bold] "rsc1_monitor_0 node2" [ style=bold color="green" fontcolor="black" ] "rsc1_monitor_0 node3" -> "probe_complete node3" [ style = bold] "rsc1_monitor_0 node3" [ style=bold color="green" fontcolor="black" ] "rsc1_stop_0 node1" -> "all_stopped" [ style = bold] "rsc1_stop_0 node1" -> "do_shutdown node1" [ style = bold] "rsc1_stop_0 node1" [ style=bold color="green" fontcolor="black" ] -"rsc2_delete_0 node2" -> "lrm_refresh node2" [ style = bold] "rsc2_delete_0 node2" -> "rsc2_start_0 node2" [ style = bold] "rsc2_delete_0 node2" [ style=bold color="green" fontcolor="black" ] "rsc2_monitor_0 node1" -> "probe_complete node1" [ style = bold] "rsc2_monitor_0 node1" [ style=bold color="green" fontcolor="black" ] "rsc2_monitor_0 node3" -> "probe_complete node3" [ style = bold] "rsc2_monitor_0 node3" [ style=bold color="green" fontcolor="black" ] "rsc2_start_0 node2" [ style=bold color="green" fontcolor="black" ] "rsc2_stop_0 node2" -> "all_stopped" [ style = bold] "rsc2_stop_0 node2" -> "rsc2_delete_0 node2" [ style = bold] "rsc2_stop_0 node2" -> "rsc2_start_0 node2" [ style = bold] "rsc2_stop_0 node2" [ style=bold color="green" fontcolor="black" ] "rsc3_monitor_0 node1" -> "probe_complete node1" [ style = bold] "rsc3_monitor_0 node1" [ style=bold color="green" fontcolor="black" ] "rsc3_monitor_0 node2" -> "probe_complete node2" [ style = bold] "rsc3_monitor_0 node2" [ style=bold color="green" fontcolor="black" ] "rsc3_start_0 node3" [ style=bold color="green" fontcolor="black" ] } diff --git a/pengine/test10/params-2.exp b/pengine/test10/params-2.exp index 29488f7450..bd60b62950 100644 --- a/pengine/test10/params-2.exp +++ b/pengine/test10/params-2.exp @@ -1,226 +1,214 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - + diff --git a/pengine/test10/params-2.summary b/pengine/test10/params-2.summary index a9ecfc2b36..2e8cf67052 100644 --- a/pengine/test10/params-2.summary +++ b/pengine/test10/params-2.summary @@ -1,37 +1,36 @@ Current cluster status: Online: [ node1 node2 node3 ] rsc1 (lsb:apache): Started node1 rsc2 (lsb:apache): Started node2 rsc3 (lsb:apache): Stopped Transition Summary: * Stop rsc1 (node1) * Restart rsc2 (Started node2) * Start rsc3 (node3) Executing cluster transition: * Resource action: rsc1 stop on node1 * Resource action: rsc1 monitor on node3 * Resource action: rsc1 monitor on node2 * Resource action: rsc2 monitor on node3 * Resource action: rsc2 monitor on node1 * Resource action: rsc3 monitor on node2 * Resource action: rsc3 monitor on node1 * Cluster action: do_shutdown on node1 * Pseudo action: probe_complete * Resource action: rsc2 stop on node2 * Resource action: rsc3 start on node3 * Pseudo action: all_stopped * Resource action: rsc2 delete on node2 - * Cluster action: lrm_refresh on node2 * Resource action: rsc2 start on node2 Revised cluster status: Online: [ node1 node2 node3 ] rsc1 (lsb:apache): Stopped rsc2 (lsb:apache): Started node2 rsc3 (lsb:apache): Started node3 diff --git a/tools/crm_simulate.c b/tools/crm_simulate.c index bd8c037fb4..5781d2990b 100644 --- a/tools/crm_simulate.c +++ b/tools/crm_simulate.c @@ -1,1521 +1,1527 @@ /* * 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 cib_t *global_cib = NULL; GListPtr op_fail = NULL; gboolean quiet = FALSE; #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 FAKE_TE_ID "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" #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, 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, char *node) { int rc = pcmk_ok; int max = strlen(rsc_template) + strlen(node) + 1; char *xpath = NULL; xmlNode *cib_object = NULL; xpath = calloc(1, max); 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(1); } if (rc == -ENXIO) { char *uuid = NULL; query_node_uuid(cib_conn, node, &uuid); cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); crm_xml_add(cib_object, XML_ATTR_UUID, uuid); crm_xml_add(cib_object, XML_ATTR_UNAME, node); cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object, cib_sync_call | cib_scope_local); free_xml(cib_object); free(uuid); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath | cib_sync_call | cib_scope_local); } 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); 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, "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->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; 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); - if (safe_str_eq(crm_element_value(action->xml, "operation"), "probe_complete")) { + if (safe_str_eq(operation, "probe_complete")) { crm_info("Skipping %s op for %s\n", crm_element_value(action->xml, "operation"), node); goto done; } if (action_rsc == NULL) { crm_log_xml_err(action->xml, "Bad"); free(node); 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); cib_node = inject_node_state(global_cib, node); CRM_ASSERT(cib_node != NULL); cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider); CRM_ASSERT(cib_resource != NULL); op = convert_graph_action(cib_resource, action, 0, target_outcome); 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) { rc = 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_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); action->confirmed = TRUE; 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) { int rc = 0; char xpath[STATUS_PATH_MAX]; char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); xmlNode *cib_node = modify_node(global_cib, target, FALSE); crm_xml_add(cib_node, XML_ATTR_ORIGIN, __FUNCTION__); CRM_ASSERT(cib_node != NULL); quiet_log(" * Fencing %s\n", target); rc = global_cib->cmds->replace(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call | cib_scope_local); CRM_ASSERT(rc == pcmk_ok); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath | cib_sync_call | cib_scope_local); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_TAG_TRANSIENT_NODEATTRS); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath | cib_sync_call | cib_scope_local); action->confirmed = TRUE; update_graph(graph, action); free_xml(cib_node); free(target); return TRUE; } static void print_cluster_status(pe_working_set_t * data_set) { char *online_nodes = NULL; char *offline_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; if (node->details->unclean) { if (node->details->online && node->details->unclean) { node_mode = "UNCLEAN (online)"; } else if (node->details->pending) { node_mode = "UNCLEAN (pending)"; } else { node_mode = "UNCLEAN (offline)"; } } else if (node->details->pending) { node_mode = "pending"; } else if (node->details->standby_onfail && node->details->online) { node_mode = "standby (on-fail)"; } else if (node->details->standby) { if (node->details->online) { node_mode = "standby"; } else { node_mode = "OFFLINE (standby)"; } } else if (node->details->online) { node_mode = "online"; online_nodes = add_list_element(online_nodes, node->details->uname); continue; } else { node_mode = "OFFLINE"; offline_nodes = add_list_element(offline_nodes, node->details->uname); continue; } if (safe_str_eq(node->details->uname, node->details->id)) { printf("Node %s: %s\n", node->details->uname, node_mode); } else { printf("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode); } } if (online_nodes) { printf("Online: [%s ]\n", online_nodes); free(online_nodes); } if (offline_nodes) { printf("OFFLINE: [%s ]\n", offline_nodes); free(offline_nodes); } 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, 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); } 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)) { action_name = g_strdup_printf("%s%s %s", prefix?prefix:"", action->task, action_host); } else if(action_host) { action_name = g_strdup_printf("%s%s %s", prefix?prefix:"", action->uuid, 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: %p", action->id, 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); 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); 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); } 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, 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); } 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); } for (gIter = node_down; gIter != NULL; gIter = gIter->next) { 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); } 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); } 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); 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) { cib_object = get_cib_copy(cib_conn); } cib_conn->cmds->signoff(cib_conn); cib_delete(cib_conn); cib_conn = NULL; if (cib_object == NULL) { fprintf(stderr, "Live CIB query failed: empty result\n"); crm_exit(3); } } 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_dtd_validation); } if (output == NULL) { char *pid = crm_itoa(getpid()); local_output = get_shadow_file(pid); 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"}, {"-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"}, {"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 *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++; 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 '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 '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) { quiet_log("\nCurrent cluster status:\n"); print_cluster_status(&data_set); } if (modified) { quiet_log("Performing requested modifications\n"); modify_configuration(&data_set, quorum, 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) { char *msg_buffer = dump_xml_formatted(data_set.graph); FILE *graph_strm = fopen(graph_file, "w"); if (graph_strm == NULL) { crm_perror(LOG_ERR, "Could not open %s for writing", graph_file); } else { if (fprintf(graph_strm, "%s\n", msg_buffer) < 0) { crm_perror(LOG_ERR, "Write to %s failed", graph_file); } fflush(graph_strm); fclose(graph_strm); } free(msg_buffer); } 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); } done: cleanup_alloc_calculations(&data_set); global_cib->cmds->signoff(global_cib); cib_delete(global_cib); free(use_date); fflush(stderr); return crm_exit(rc); }