diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c index 7074dbfaec..cbb2f55916 100644 --- a/lib/pengine/utils.c +++ b/lib/pengine/utils.c @@ -1,1437 +1,1495 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root); void print_str_str(gpointer key, gpointer value, gpointer user_data); gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data); void unpack_operation( action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set); void pe_free_shallow(GListPtr alist) { pe_free_shallow_adv(alist, TRUE); } void pe_free_shallow_adv(GListPtr alist, gboolean with_data) { GListPtr item; GListPtr item_next = alist; if(with_data == FALSE && alist != NULL) { g_list_free(alist); return; } while(item_next != NULL) { item = item_next; item_next = item_next->next; if(with_data) { /* crm_debug_5("freeing %p", item->data); */ crm_free(item->data); } item->data = NULL; item->next = NULL; g_list_free_1(item); } } node_t * node_copy(node_t *this_node) { node_t *new_node = NULL; CRM_CHECK(this_node != NULL, return NULL); crm_malloc0(new_node, sizeof(node_t)); CRM_ASSERT(new_node != NULL); crm_debug_5("Copying %p (%s) to %p", this_node, this_node->details->uname, new_node); new_node->weight = this_node->weight; new_node->fixed = this_node->fixed; new_node->details = this_node->details; return new_node; } /* are the contents of list1 and list2 equal * nodes with weight < 0 are ignored if filter == TRUE * * slow but linear * */ gboolean node_list_eq(GListPtr list1, GListPtr list2, gboolean filter) { node_t *other_node; GListPtr lhs = list1; GListPtr rhs = list2; slist_iter( node, node_t, lhs, lpc, if(node == NULL || (filter && node->weight < 0)) { continue; } other_node = (node_t*) pe_find_node_id(rhs, node->details->id); if(other_node == NULL || other_node->weight < 0) { return FALSE; } ); lhs = list2; rhs = list1; slist_iter( node, node_t, lhs, lpc, if(node == NULL || (filter && node->weight < 0)) { continue; } other_node = (node_t*) pe_find_node_id(rhs, node->details->id); if(other_node == NULL || other_node->weight < 0) { return FALSE; } ); return TRUE; } /* any node in list1 or list2 and not in the other gets a score of -INFINITY */ GListPtr node_list_exclude(GListPtr list1, GListPtr list2) { node_t *other_node = NULL; GListPtr result = NULL; result = node_list_dup(list1, FALSE, FALSE); slist_iter( node, node_t, result, lpc, other_node = pe_find_node_id(list2, node->details->id); if(other_node == NULL) { node->weight = -INFINITY; } else { node->weight = merge_weights(node->weight, other_node->weight); } ); slist_iter( node, node_t, list2, lpc, other_node = pe_find_node_id(result, node->details->id); if(other_node == NULL) { node_t *new_node = node_copy(node); new_node->weight = -INFINITY; result = g_list_append(result, new_node); } ); return result; } /* the intersection of list1 and list2 */ GListPtr node_list_and(GListPtr list1, GListPtr list2, gboolean filter) { GListPtr result = NULL; unsigned lpc = 0; for(lpc = 0; lpc < g_list_length(list1); lpc++) { node_t *node = (node_t*)g_list_nth_data(list1, lpc); node_t *other_node = pe_find_node_id(list2, node->details->id); node_t *new_node = NULL; if(other_node != NULL) { new_node = node_copy(node); } if(new_node != NULL) { crm_debug_4("%s: %d + %d", node->details->uname, other_node->weight, new_node->weight); new_node->weight = merge_weights( new_node->weight, other_node->weight); crm_debug_3("New node weight for %s: %d", new_node->details->uname, new_node->weight); if(filter && new_node->weight < 0) { crm_free(new_node); new_node = NULL; } } if(new_node != NULL) { result = g_list_append(result, new_node); } } return result; } /* list1 - list2 */ GListPtr node_list_minus(GListPtr list1, GListPtr list2, gboolean filter) { GListPtr result = NULL; slist_iter( node, node_t, list1, lpc, node_t *other_node = pe_find_node_id(list2, node->details->id); node_t *new_node = NULL; if(node == NULL || other_node != NULL || (filter && node->weight < 0)) { continue; } new_node = node_copy(node); result = g_list_append(result, new_node); ); crm_debug_3("Minus result len: %d", g_list_length(result)); return result; } /* list1 + list2 - (intersection of list1 and list2) */ GListPtr node_list_xor(GListPtr list1, GListPtr list2, gboolean filter) { GListPtr result = NULL; slist_iter( node, node_t, list1, lpc, node_t *new_node = NULL; node_t *other_node = (node_t*) pe_find_node_id(list2, node->details->id); if(node == NULL || other_node != NULL || (filter && node->weight < 0)) { continue; } new_node = node_copy(node); result = g_list_append(result, new_node); ); slist_iter( node, node_t, list2, lpc, node_t *new_node = NULL; node_t *other_node = (node_t*) pe_find_node_id(list1, node->details->id); if(node == NULL || other_node != NULL || (filter && node->weight < 0)) { continue; } new_node = node_copy(node); result = g_list_append(result, new_node); ); crm_debug_3("Xor result len: %d", g_list_length(result)); return result; } GListPtr node_list_or(GListPtr list1, GListPtr list2, gboolean filter) { node_t *other_node = NULL; GListPtr result = NULL; gboolean needs_filter = FALSE; result = node_list_dup(list1, FALSE, filter); slist_iter( node, node_t, list2, lpc, if(node == NULL) { continue; } other_node = (node_t*)pe_find_node_id( result, node->details->id); if(other_node != NULL) { crm_debug_4("%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); if(filter && node->weight < 0) { needs_filter = TRUE; } } else if(filter == FALSE || node->weight >= 0) { node_t *new_node = node_copy(node); result = g_list_append(result, new_node); } ); /* not the neatest way, but the most expedient for now */ if(filter && needs_filter) { GListPtr old_result = result; result = node_list_dup(old_result, FALSE, filter); pe_free_shallow_adv(old_result, TRUE); } return result; } GListPtr node_list_dup(GListPtr list1, gboolean reset, gboolean filter) { GListPtr result = NULL; slist_iter( this_node, node_t, list1, lpc, node_t *new_node = NULL; if(filter && this_node->weight < 0) { continue; } new_node = node_copy(this_node); if(reset) { new_node->weight = 0; } if(new_node != NULL) { result = g_list_append(result, new_node); } ); return result; } void dump_node_scores(int level, resource_t *rsc, const char *comment, GListPtr nodes) { GListPtr list = nodes; if(rsc) { list = rsc->allowed_nodes; } if(is_set(rsc->flags, pe_rsc_orphan)) { /* Don't show the allocation scores for orphans */ return; } slist_iter( node, node_t, list, lpc, if(level == 0) { if(rsc) { fprintf(stdout, "%s: %s allocation score on %s: %d\n", comment, rsc->id, node->details->uname, node->weight); } else { fprintf(stdout, "%s: %s = %d\n", comment, node->details->uname, node->weight); } } else { if(rsc) { do_crm_log_unlikely(level, "%s: %s allocation score on %s: %d", comment, rsc->id, node->details->uname, node->weight); } else { do_crm_log_unlikely(level, "%s: %s = %d", comment, node->details->uname, node->weight); } } ); if(rsc && rsc->children) { slist_iter( child, resource_t, rsc->children, lpc, dump_node_scores(level, child, comment, nodes); ); } } +static void +append_dump_text(gpointer key, gpointer value, gpointer user_data) +{ + char **dump_text = user_data; + int len = 0; + char *new_text = NULL; + + len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1; + crm_malloc0(new_text, len); + sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value); + + free(*dump_text); + *dump_text = new_text; +} + +void +dump_node_capacity(int level, const char *comment, node_t *node) +{ + int len = 0; + char *dump_text = NULL; + + len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1; + crm_malloc0(dump_text, len); + sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname); + + g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text); + + if(level == 0) { + fprintf(stdout, "%s\n", dump_text); + } else { + do_crm_log_unlikely(level, "%s", dump_text); + } + + crm_free(dump_text); +} + +void +dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node) +{ + int len = 0; + char *dump_text = NULL; + + len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ") + + strlen(node->details->uname) + strlen(":") + 1; + crm_malloc0(dump_text, len); + sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname); + + g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text); + + if(level == 0) { + fprintf(stdout, "%s\n", dump_text); + } else { + do_crm_log_unlikely(level, "%s", dump_text); + } + + crm_free(dump_text); +} + gint sort_rsc_index(gconstpointer a, gconstpointer b) { const resource_t *resource1 = (const resource_t*)a; const resource_t *resource2 = (const resource_t*)b; if(a == NULL && b == NULL) { return 0; } if(a == NULL) { return 1; } if(b == NULL) { return -1; } if(resource1->sort_index > resource2->sort_index) { return -1; } if(resource1->sort_index < resource2->sort_index) { return 1; } return 0; } gint sort_rsc_priority(gconstpointer a, gconstpointer b) { const resource_t *resource1 = (const resource_t*)a; const resource_t *resource2 = (const resource_t*)b; if(a == NULL && b == NULL) { return 0; } if(a == NULL) { return 1; } if(b == NULL) { return -1; } if(resource1->priority > resource2->priority) { return -1; } if(resource1->priority < resource2->priority) { return 1; } return 0; } action_t * custom_action(resource_t *rsc, char *key, const char *task, node_t *on_node, gboolean optional, gboolean save_action, pe_working_set_t *data_set) { action_t *action = NULL; GListPtr possible_matches = NULL; CRM_CHECK(key != NULL, return NULL); CRM_CHECK(task != NULL, return NULL); if(save_action && rsc != NULL) { possible_matches = find_actions(rsc->actions, key, on_node); } if(possible_matches != NULL) { crm_free(key); if(g_list_length(possible_matches) > 1) { pe_warn("Action %s for %s on %s exists %d times", task, rsc?rsc->id:"", on_node?on_node->details->uname:"", g_list_length(possible_matches)); } action = g_list_nth_data(possible_matches, 0); crm_debug_4("Found existing action (%d) %s for %s on %s", action->id, task, rsc?rsc->id:"", on_node?on_node->details->uname:""); g_list_free(possible_matches); } if(action == NULL) { if(save_action) { crm_debug_4("Creating%s action %d: %s for %s on %s", optional?"":" manditory", data_set->action_id, key, rsc?rsc->id:"", on_node?on_node->details->uname:""); } crm_malloc0(action, sizeof(action_t)); if(save_action) { action->id = data_set->action_id++; } else { action->id = 0; } action->rsc = rsc; CRM_ASSERT(task != NULL); action->task = crm_strdup(task); action->node = on_node; action->uuid = key; action->failure_is_fatal = TRUE; action->runnable = TRUE; action->optional = optional; /* Implied by crm_malloc0()... action->actions_before = NULL; action->actions_after = NULL; action->pseudo = FALSE; action->dumped = FALSE; action->processed = FALSE; action->seen_count = 0; */ action->extra = g_hash_table_new_full( g_str_hash, g_str_equal, free, free); action->meta = g_hash_table_new_full( g_str_hash, g_str_equal, free, free); if(save_action) { data_set->actions = g_list_append( data_set->actions, action); } if(rsc != NULL) { action->op_entry = find_rsc_op_entry(rsc, key); unpack_operation( action, action->op_entry, data_set); if(save_action) { rsc->actions = g_list_append( rsc->actions, action); } } if(save_action) { crm_debug_4("Action %d created", action->id); } } if(optional == FALSE && action->optional) { crm_debug_2("Action %d (%s) marked manditory", action->id, action->uuid); action->optional = FALSE; } if(rsc != NULL) { enum action_tasks a_task = text2task(action->task); int warn_level = LOG_DEBUG_3; if(save_action) { warn_level = LOG_WARNING; } if(action->have_node_attrs == FALSE && action->node != NULL && action->op_entry != NULL) { action->have_node_attrs = TRUE; unpack_instance_attributes( data_set->input, action->op_entry, XML_TAG_ATTR_SETS, action->node->details->attrs, action->extra, NULL, FALSE, data_set->now); } if(action->pseudo) { /* leave untouched */ } else if(action->node == NULL) { action->runnable = FALSE; } else if(is_not_set(rsc->flags, pe_rsc_managed) && g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) { do_crm_log_unlikely(LOG_DEBUG, "Action %s (unmanaged)", action->uuid); action->optional = TRUE; /* action->runnable = FALSE; */ } else if(action->node->details->online == FALSE) { action->runnable = FALSE; do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)", action->uuid, action->node->details->uname); if(is_set(action->rsc->flags, pe_rsc_managed) && save_action && a_task == stop_rsc) { do_crm_log(warn_level, "Marking node %s unclean", action->node->details->uname); action->node->details->unclean = TRUE; } } else if(action->node->details->pending) { action->runnable = FALSE; do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)", action->uuid, action->node->details->uname); } else if(action->needs == rsc_req_nothing) { crm_debug_3("Action %s doesnt require anything", action->uuid); action->runnable = TRUE; #if 0 /* * No point checking this * - if we dont have quorum we cant stonith anyway */ } else if(action->needs == rsc_req_stonith) { crm_debug_3("Action %s requires only stonith", action->uuid); action->runnable = TRUE; #endif } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE && data_set->no_quorum_policy == no_quorum_stop) { action->runnable = FALSE; crm_debug("%s\t%s (cancelled : quorum)", action->node->details->uname, action->uuid); } else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE && data_set->no_quorum_policy == no_quorum_freeze) { crm_debug_3("Check resource is already active"); if(rsc->fns->active(rsc, TRUE) == FALSE) { action->runnable = FALSE; crm_debug("%s\t%s (cancelled : quorum freeze)", action->node->details->uname, action->uuid); } } else { crm_debug_3("Action %s is runnable", action->uuid); action->runnable = TRUE; } if(save_action) { switch(a_task) { case stop_rsc: set_bit(rsc->flags, pe_rsc_stopping); break; case start_rsc: clear_bit(rsc->flags, pe_rsc_starting); if(action->runnable) { set_bit(rsc->flags, pe_rsc_starting); } break; default: break; } } } return action; } void unpack_operation( action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set) { int value_i = 0; unsigned long long interval = 0; unsigned long long start_delay = 0; char *value_ms = NULL; const char *class = NULL; const char *value = NULL; const char *field = NULL; CRM_CHECK(action->rsc != NULL, return); unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL, action->meta, NULL, FALSE, data_set->now); xml_prop_iter(xml_obj, name, value, if(value != NULL && g_hash_table_lookup(action->meta, name) == NULL) { g_hash_table_insert(action->meta, crm_strdup(name), crm_strdup(value)); } ); unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS, NULL, action->meta, NULL, FALSE, data_set->now); unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS, NULL, action->meta, NULL, FALSE, data_set->now); g_hash_table_remove(action->meta, "id"); class = g_hash_table_lookup(action->rsc->meta, "class"); value = g_hash_table_lookup(action->meta, "requires"); if(safe_str_eq(class, "stonith")) { action->needs = rsc_req_nothing; value = "nothing (fencing op)"; } else if(value == NULL && safe_str_neq(action->task, CRMD_ACTION_START)) { action->needs = rsc_req_nothing; value = "nothing (default)"; } else if(safe_str_eq(value, "nothing")) { action->needs = rsc_req_nothing; } else if(safe_str_eq(value, "quorum")) { action->needs = rsc_req_quorum; } else if(safe_str_eq(value, "fencing")) { action->needs = rsc_req_stonith; } else if(data_set->no_quorum_policy == no_quorum_ignore || safe_str_eq(class, "stonith")) { action->needs = rsc_req_nothing; value = "nothing (default)"; } else if(data_set->no_quorum_policy == no_quorum_freeze && is_set(data_set->flags, pe_flag_stonith_enabled)) { action->needs = rsc_req_stonith; value = "fencing (default)"; } else { action->needs = rsc_req_quorum; value = "quorum (default)"; } crm_debug_3("\tAction %s requires: %s", action->task, value); value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL); if(safe_str_eq(action->task, CRMD_ACTION_STOP) && safe_str_eq(value, "standby")) { crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id); value = NULL; } if(value == NULL) { } else if(safe_str_eq(value, "block")) { action->on_fail = action_fail_block; } else if(safe_str_eq(value, "fence")) { action->on_fail = action_fail_fence; value = "node fencing"; if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) { crm_config_err("Specifying on_fail=fence and" " stonith-enabled=false makes no sense"); action->on_fail = action_fail_stop; action->fail_role = RSC_ROLE_STOPPED; value = "stop resource"; } } else if(safe_str_eq(value, "standby")) { action->on_fail = action_fail_standby; value = "node standby"; } else if(safe_str_eq(value, "ignore") || safe_str_eq(value, "nothing")) { action->on_fail = action_fail_ignore; value = "ignore"; } else if(safe_str_eq(value, "migrate")) { action->on_fail = action_fail_migrate; value = "force migration"; } else if(safe_str_eq(value, "stop")) { action->on_fail = action_fail_stop; action->fail_role = RSC_ROLE_STOPPED; value = "stop resource"; } else if(safe_str_eq(value, "restart")) { action->on_fail = action_fail_recover; value = "restart (and possibly migrate)"; } else { pe_err("Resource %s: Unknown failure type (%s)", action->rsc->id, value); value = NULL; } /* defaults */ if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) { if(is_set(data_set->flags, pe_flag_stonith_enabled)) { action->on_fail = action_fail_fence; value = "resource fence (default)"; } else { action->on_fail = action_fail_block; value = "resource block (default)"; } } else if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_MIGRATED)) { action->on_fail = action_migrate_failure; value = "atomic migration recovery (default)"; } else if(value == NULL) { action->on_fail = action_fail_recover; value = "restart (and possibly migrate) (default)"; } crm_debug_3("\t%s failure handling: %s", action->task, value); value = NULL; if(xml_obj != NULL) { value = g_hash_table_lookup(action->meta, "role_after_failure"); } if(value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) { action->fail_role = text2role(value); } /* defaults */ if(action->fail_role == RSC_ROLE_UNKNOWN) { if(safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) { action->fail_role = RSC_ROLE_SLAVE; } else { action->fail_role = RSC_ROLE_STARTED; } } crm_debug_3("\t%s failure results in: %s", action->task, role2text(action->fail_role)); field = XML_LRM_ATTR_INTERVAL; value = g_hash_table_lookup(action->meta, field); if(value != NULL) { interval = crm_get_interval(value); if(interval > 0) { value_ms = crm_itoa(interval); g_hash_table_replace(action->meta, crm_strdup(field), value_ms); } else { g_hash_table_remove(action->meta, field); } } field = XML_OP_ATTR_START_DELAY; value = g_hash_table_lookup(action->meta, field); if(value != NULL) { value_i = crm_get_msec(value); if(value_i < 0) { value_i = 0; } start_delay = value_i; value_ms = crm_itoa(value_i); g_hash_table_replace(action->meta, crm_strdup(field), value_ms); } else if(interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) { char *date_str = NULL; char *date_str_mutable = NULL; ha_time_t *origin = NULL; value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN); date_str = crm_strdup(value); date_str_mutable = date_str; origin = parse_date(&date_str_mutable); crm_free(date_str); if(origin == NULL) { crm_config_err("Operation %s contained an invalid "XML_OP_ATTR_ORIGIN": %s", ID(xml_obj), value); } else { ha_time_t *delay = NULL; int rc = compare_date(origin, data_set->now); unsigned long long delay_s = 0; while(rc < 0) { add_seconds(origin, interval/1000); rc = compare_date(origin, data_set->now); } delay = subtract_time(origin, data_set->now); delay_s = date_in_seconds(delay); /* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */ crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj)); g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY), crm_itoa(delay_s * 1000)); start_delay = delay_s * 1000; free_ha_date(origin); free_ha_date(delay); } } field = XML_ATTR_TIMEOUT; value = g_hash_table_lookup(action->meta, field); if(value == NULL) { value = pe_pref( data_set->config_hash, "default-action-timeout"); } value_i = crm_get_msec(value); if(value_i < 0) { value_i = 0; } value_i += start_delay; value_ms = crm_itoa(value_i); g_hash_table_replace(action->meta, crm_strdup(field), value_ms); } xmlNode * find_rsc_op_entry(resource_t *rsc, const char *key) { int number = 0; const char *name = NULL; const char *value = NULL; const char *interval = NULL; char *match_key = NULL; xmlNode *op = NULL; xml_child_iter_filter( rsc->ops_xml, operation, "op", name = crm_element_value(operation, "name"); interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL); value = crm_element_value(operation, "enabled"); if(value && crm_is_true(value) == FALSE) { continue; } number = crm_get_interval(interval); if(number < 0) { continue; } match_key = generate_op_key(rsc->id, name, number); if(safe_str_eq(key, match_key)) { op = operation; } crm_free(match_key); if(op != NULL) { return op; } ); if(strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) { match_key = generate_op_key(rsc->id, "migrate", 0); op = find_rsc_op_entry(rsc, match_key); crm_free(match_key); } if(op == NULL) { crm_debug_3("No match for %s", key); } return op; } void print_node(const char *pre_text, node_t *node, gboolean details) { if(node == NULL) { crm_debug_4("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_4("%s%s%sNode %s: (weight=%d, fixed=%s)", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", node->details==NULL?"error ":node->details->online?"":"Unavailable/Unclean ", node->details->uname, node->weight, node->fixed?"True":"False"); if(details && node != NULL && node->details != NULL) { char *pe_mutable = crm_strdup("\t\t"); crm_debug_4("\t\t===Node Attributes"); g_hash_table_foreach(node->details->attrs, print_str_str, pe_mutable); crm_free(pe_mutable); crm_debug_4("\t\t=== Resources"); slist_iter( rsc, resource_t, node->details->running_rsc, lpc, print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE); ); } } /* * Used by the HashTable for-loop */ void print_str_str(gpointer key, gpointer value, gpointer user_data) { crm_debug_4("%s%s %s ==> %s", user_data==NULL?"":(char*)user_data, user_data==NULL?"":": ", (char*)key, (char*)value); } void print_resource( int log_level, const char *pre_text, resource_t *rsc, gboolean details) { long options = pe_print_log; if(rsc == NULL) { do_crm_log(log_level-1, "%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } if(details) { options |= pe_print_details; } rsc->fns->print(rsc, pre_text, options, &log_level); } void pe_free_action(action_t *action) { if(action == NULL) { return; } pe_free_shallow(action->actions_before);/* action_warpper_t* */ pe_free_shallow(action->actions_after); /* action_warpper_t* */ if(action->extra) { g_hash_table_destroy(action->extra); } if(action->meta) { g_hash_table_destroy(action->meta); } crm_free(action->task); crm_free(action->uuid); crm_free(action); } GListPtr find_recurring_actions(GListPtr input, node_t *not_on_node) { const char *value = NULL; GListPtr result = NULL; CRM_CHECK(input != NULL, return NULL); slist_iter( action, action_t, input, lpc, value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL); if(value == NULL) { /* skip */ } else if(safe_str_eq(value, "0")) { /* skip */ } else if(safe_str_eq(CRMD_ACTION_CANCEL, action->task)) { /* skip */ } else if(not_on_node == NULL) { crm_debug_5("(null) Found: %s", action->uuid); result = g_list_append(result, action); } else if(action->node == NULL) { /* skip */ } else if(action->node->details != not_on_node->details) { crm_debug_5("Found: %s", action->uuid); result = g_list_append(result, action); } ); return result; } action_t * find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node) { CRM_CHECK(uuid || task, return NULL); slist_iter( action, action_t, input, lpc, if(uuid != NULL && safe_str_neq(uuid, action->uuid)) { continue; } else if(task != NULL && safe_str_neq(task, action->task)) { continue; } else if(on_node == NULL) { return action; } else if(action->node == NULL) { continue; } else if(on_node->details == action->node->details) { return action; } ); return NULL; } GListPtr find_actions(GListPtr input, const char *key, node_t *on_node) { GListPtr result = NULL; CRM_CHECK(key != NULL, return NULL); slist_iter( action, action_t, input, lpc, crm_debug_5("Matching %s against %s", key, action->uuid); if(safe_str_neq(key, action->uuid)) { continue; } else if(on_node == NULL) { result = g_list_append(result, action); } else if(action->node == NULL) { /* skip */ crm_debug_2("While looking for %s action on %s, " "found an unallocated one. Assigning" " it to the requested node...", key, on_node->details->uname); action->node = on_node; result = g_list_append(result, action); } else if(on_node->details == action->node->details) { result = g_list_append(result, action); } ); return result; } GListPtr find_actions_exact(GListPtr input, const char *key, node_t *on_node) { GListPtr result = NULL; CRM_CHECK(key != NULL, return NULL); slist_iter( action, action_t, input, lpc, crm_debug_5("Matching %s against %s", key, action->uuid); if(safe_str_neq(key, action->uuid)) { crm_debug_3("Key mismatch: %s vs. %s", key, action->uuid); continue; } else if(on_node == NULL || action->node == NULL) { crm_debug_3("on_node=%p, action->node=%p", on_node, action->node); continue; } else if(safe_str_eq(on_node->details->id, action->node->details->id)) { result = g_list_append(result, action); } crm_debug_2("Node mismatch: %s vs. %s", on_node->details->id, action->node->details->id); ); return result; } void set_id(xmlNode * xml_obj, const char *prefix, int child) { int id_len = 0; gboolean use_prefix = TRUE; gboolean use_child = TRUE; char *new_id = NULL; const char *id = crm_element_value(xml_obj, XML_ATTR_ID); id_len = 1 + strlen(id); if(child > 999) { pe_err("Are you insane?!?" " The CRM does not support > 1000 children per resource"); return; } else if(child < 0) { use_child = FALSE; } else { id_len += 4; /* child */ } if(prefix == NULL || safe_str_eq(id, prefix)) { use_prefix = FALSE; } else { id_len += (1 + strlen(prefix)); } crm_malloc0(new_id, id_len); if(use_child) { snprintf(new_id, id_len, "%s%s%s:%d", use_prefix?prefix:"", use_prefix?":":"", id, child); } else { snprintf(new_id, id_len, "%s%s%s", use_prefix?prefix:"", use_prefix?":":"", id); } crm_xml_add(xml_obj, XML_ATTR_ID, new_id); crm_free(new_id); } static void resource_node_score(resource_t *rsc, node_t *node, int score, const char *tag) { node_t *match = NULL; if(rsc->children) { slist_iter( child_rsc, resource_t, rsc->children, lpc, resource_node_score(child_rsc, node, score, tag); ); } crm_debug_2("Setting %s for %s on %s: %d", tag, rsc->id, node->details->uname, score); match = pe_find_node_id(rsc->allowed_nodes, node->details->id); if(match == NULL) { match = node_copy(node); match->weight = 0; rsc->allowed_nodes = g_list_append(rsc->allowed_nodes, match); } match->weight = merge_weights(match->weight, score); } void resource_location(resource_t *rsc, node_t *node, int score, const char *tag, pe_working_set_t *data_set) { if(node != NULL) { resource_node_score(rsc, node, score, tag); } else if(data_set != NULL) { slist_iter( node, node_t, data_set->nodes, lpc, resource_node_score(rsc, node, score, tag); ); } else { slist_iter( node, node_t, rsc->allowed_nodes, lpc, resource_node_score(rsc, node, score, tag); ); } if(node == NULL && score == -INFINITY) { if(rsc->allocated_to) { crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname); crm_free(rsc->allocated_to); rsc->allocated_to = NULL; } } } #define sort_return(an_int) crm_free(a_uuid); crm_free(b_uuid); return an_int gint sort_op_by_callid(gconstpointer a, gconstpointer b) { char *a_uuid = NULL; char *b_uuid = NULL; const xmlNode *xml_a = a; const xmlNode *xml_b = b; const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID); const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID); const char *a_task_id = crm_element_value_const(xml_a, XML_LRM_ATTR_CALLID); const char *b_task_id = crm_element_value_const(xml_b, XML_LRM_ATTR_CALLID); const char *a_key = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC); const char *b_key = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC); int dummy = -1; int a_id = -1; int b_id = -1; int a_rc = -1; int b_rc = -1; int a_status = -1; int b_status = -1; int a_call_id = -1; int b_call_id = -1; if(safe_str_eq(a_xml_id, b_xml_id)) { /* We have duplicate lrm_rsc_op entries in the status * section which is unliklely to be a good thing * - we can handle it easily enough, but we need to get * to the bottom of why its happening. */ pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id); sort_return(0); } CRM_CHECK(a_task_id != NULL && b_task_id != NULL, crm_err("a: %s, b: %s", crm_str(a_xml_id), crm_str(b_xml_id)); sort_return(0)); a_call_id = crm_parse_int(a_task_id, NULL); b_call_id = crm_parse_int(b_task_id, NULL); if(a_call_id == -1 && b_call_id == -1) { /* both are pending ops so it doesnt matter since * stops are never pending */ sort_return(0); } else if(a_call_id >= 0 && a_call_id < b_call_id) { crm_debug_4("%s (%d) < %s (%d) : call id", a_xml_id, a_call_id, b_xml_id, b_call_id); sort_return(-1); } else if(b_call_id >= 0 && a_call_id > b_call_id) { crm_debug_4("%s (%d) > %s (%d) : call id", a_xml_id, a_call_id, b_xml_id, b_call_id); sort_return(1); } crm_debug_5("%s (%d) == %s (%d) : continuing", a_xml_id, a_call_id, b_xml_id, b_call_id); /* now process pending ops */ CRM_CHECK(a_key != NULL && b_key != NULL, sort_return(0)); CRM_CHECK(decode_transition_magic( a_key, &a_uuid, &a_id, &dummy, &a_status, &a_rc, &dummy), sort_return(0)); CRM_CHECK(decode_transition_magic( b_key, &b_uuid, &b_id, &dummy, &b_status, &b_rc, &dummy), sort_return(0)); /* try and determin the relative age of the operation... * some pending operations (ie. a start) may have been supuerceeded * by a subsequent stop * * [a|b]_id == -1 means its a shutdown operation and _always_ comes last */ if(safe_str_neq(a_uuid, b_uuid) || a_id == b_id) { /* * some of the logic in here may be redundant... * * if the UUID from the TE doesnt match then one better * be a pending operation. * pending operations dont survive between elections and joins * because we query the LRM directly */ CRM_CHECK(a_call_id == -1 || b_call_id == -1, crm_err("a: %s=%d, b: %s=%d", crm_str(a_xml_id), a_call_id, crm_str(b_xml_id), b_call_id); sort_return(0)); CRM_CHECK(a_call_id >= 0 || b_call_id >= 0, sort_return(0)); if(b_call_id == -1) { crm_debug_2("%s (%d) < %s (%d) : transition + call id", a_xml_id, a_call_id, b_xml_id, b_call_id); sort_return(-1); } if(a_call_id == -1) { crm_debug_2("%s (%d) > %s (%d) : transition + call id", a_xml_id, a_call_id, b_xml_id, b_call_id); sort_return(1); } } else if((a_id >= 0 && a_id < b_id) || b_id == -1) { crm_debug_3("%s (%d) < %s (%d) : transition", a_xml_id, a_id, b_xml_id, b_id); sort_return(-1); } else if((b_id >= 0 && a_id > b_id) || a_id == -1) { crm_debug_3("%s (%d) > %s (%d) : transition", a_xml_id, a_id, b_xml_id, b_id); sort_return(1); } /* we should never end up here */ crm_err("%s (%d:%d:%s) ?? %s (%d:%d:%s) : default", a_xml_id, a_call_id, a_id, a_uuid, b_xml_id, b_call_id, b_id, b_uuid); CRM_CHECK(FALSE, sort_return(0)); } time_t get_timet_now(pe_working_set_t *data_set) { time_t now = 0; if(data_set && data_set->now) { now = data_set->now->tm_now; } if(now == 0) { /* eventually we should convert data_set->now into time_tm * for now, its only triggered by PE regression tests */ now = time(NULL); crm_crit("Defaulting to 'now'"); if(data_set && data_set->now) { data_set->now->tm_now = now; } } return now; } int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set) { int last = 0; int fail_count = 0; resource_t *failed = rsc; char *fail_attr = crm_concat("fail-count", rsc->id, '-'); const char *value = g_hash_table_lookup(node->details->attrs, fail_attr); if(is_not_set(rsc->flags, pe_rsc_unique)) { failed = uber_parent(rsc); } if(value != NULL) { fail_count = char2score(value); crm_info("%s has failed %d times on %s", failed->id, fail_count, node->details->uname); } crm_free(fail_attr); fail_attr = crm_concat("last-failure", rsc->id, '-'); value = g_hash_table_lookup(node->details->attrs, fail_attr); if(value != NULL && rsc->failure_timeout) { last = crm_parse_int(value, NULL); if(last_failure) { *last_failure = last; } if(last > 0) { time_t now = get_timet_now(data_set); if(now > (last + rsc->failure_timeout)) { crm_notice("Failcount for %s on %s has expired (limit was %ds)", failed->id, node->details->uname, rsc->failure_timeout); fail_count = 0; } } } crm_free(fail_attr); return fail_count; } gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role) { const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE); CRM_CHECK(role != NULL, return FALSE); if(value == NULL || safe_str_eq("started", value) || safe_str_eq("default", value)) { return FALSE; } *role = text2role(value); if(*role == RSC_ROLE_UNKNOWN) { crm_config_err("%s: Unknown value for %s: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value); return FALSE; } else if(*role > RSC_ROLE_STARTED) { const char *stateful = g_hash_table_lookup(rsc->meta, "stateful"); if(rsc->variant == pe_master) { /* Next check isn't true during common_unpack() for the master */ } else if(crm_is_true(stateful) == FALSE) { pe_warn("%s is not part of a master/slave resource, a %s of '%s' makes no sense: %s", rsc->id, XML_RSC_ATTR_TARGET_ROLE, value, stateful); *role = RSC_ROLE_STARTED; return FALSE; } } return TRUE; } diff --git a/lib/pengine/utils.h b/lib/pengine/utils.h index 9d55d9531d..92669fb72b 100644 --- a/lib/pengine/utils.h +++ b/lib/pengine/utils.h @@ -1,129 +1,132 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PE_UTILS__H #define PE_UTILS__H #include #include extern node_t *node_copy(node_t *this_node) ; extern time_t get_timet_now(pe_working_set_t *data_set); extern int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set); /* Binary like operators for lists of nodes */ extern GListPtr node_list_exclude(GListPtr list1, GListPtr list2); extern GListPtr node_list_dup(GListPtr list1, gboolean reset, gboolean filter); extern GListPtr node_list_and(GListPtr list1, GListPtr list2, gboolean filter); extern GListPtr node_list_xor(GListPtr list1, GListPtr list2, gboolean filter); extern GListPtr node_list_minus(GListPtr list1,GListPtr list2,gboolean filter); extern gboolean node_list_eq(GListPtr list1, GListPtr list2, gboolean filter); extern GListPtr node_list_or(GListPtr list1, GListPtr list2, gboolean filter); extern void pe_free_shallow(GListPtr alist); extern void pe_free_shallow_adv(GListPtr alist, gboolean with_data); /* For creating the transition graph */ extern xmlNode *action2xml(action_t *action, gboolean as_input); /* Printing functions for debug */ extern void print_node( const char *pre_text, node_t *node, gboolean details); extern void print_resource( int log_level, const char *pre_text, resource_t *rsc, gboolean details); extern void dump_node_scores(int level, resource_t *rsc, const char *comment, GListPtr nodes); +extern void dump_node_capacity(int level, const char *comment, node_t *node); +extern void dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node); + /* Sorting functions */ extern gint sort_rsc_priority(gconstpointer a, gconstpointer b); extern gint sort_rsc_index(gconstpointer a, gconstpointer b); extern xmlNode *find_rsc_op_entry(resource_t *rsc, const char *key); extern action_t *custom_action( resource_t *rsc, char *key, const char *task, node_t *on_node, gboolean optional, gboolean foo, pe_working_set_t *data_set); #define delete_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DELETE, 0) #define delete_action(rsc, node, optional) custom_action( \ rsc, delete_key(rsc), CRMD_ACTION_DELETE, node, \ optional, TRUE, data_set); #define stopped_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOPPED, 0) #define stopped_action(rsc, node, optional) custom_action( \ rsc, stopped_key(rsc), CRMD_ACTION_STOPPED, node, \ optional, TRUE, data_set); #define stop_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOP, 0) #define stop_action(rsc, node, optional) custom_action( \ rsc, stop_key(rsc), CRMD_ACTION_STOP, node, \ optional, TRUE, data_set); #define start_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_START, 0) #define start_action(rsc, node, optional) custom_action( \ rsc, start_key(rsc), CRMD_ACTION_START, node, \ optional, TRUE, data_set) #define started_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STARTED, 0) #define started_action(rsc, node, optional) custom_action( \ rsc, started_key(rsc), CRMD_ACTION_STARTED, node, \ optional, TRUE, data_set) #define promote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTE, 0) #define promote_action(rsc, node, optional) custom_action( \ rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, node, \ optional, TRUE, data_set) #define promoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTED, 0) #define promoted_action(rsc, node, optional) custom_action( \ rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, node, \ optional, TRUE, data_set) #define demote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTE, 0) #define demote_action(rsc, node, optional) custom_action( \ rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, node, \ optional, TRUE, data_set) #define demoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTED, 0) #define demoted_action(rsc, node, optional) custom_action( \ rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, node, \ optional, TRUE, data_set) extern action_t *find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node); extern GListPtr find_actions(GListPtr input, const char *key, node_t *on_node); extern GListPtr find_actions_exact( GListPtr input, const char *key, node_t *on_node); extern GListPtr find_recurring_actions(GListPtr input, node_t *not_on_node); extern void set_id(xmlNode *xml_obj, const char *prefix, int child); extern void pe_free_action(action_t *action); extern void resource_location(resource_t *rsc, node_t *node, int score, const char *tag, pe_working_set_t *data_set); extern gint sort_op_by_callid(gconstpointer a, gconstpointer b); extern gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role); #endif diff --git a/pengine/allocate.c b/pengine/allocate.c index f9c6c97a23..1189c461cb 100644 --- a/pengine/allocate.c +++ b/pengine/allocate.c @@ -1,1684 +1,1694 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include 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_order_lh, native_rsc_order_rh, native_rsc_location, native_expand, complex_migrate_reload, complex_stonith_ordering, }, { group_merge_weights, group_color, group_create_actions, native_create_probe, group_internal_constraints, group_rsc_colocation_lh, group_rsc_colocation_rh, group_rsc_order_lh, group_rsc_order_rh, group_rsc_location, group_expand, complex_migrate_reload, complex_stonith_ordering, }, { native_merge_weights, clone_color, clone_create_actions, clone_create_probe, clone_internal_constraints, clone_rsc_colocation_lh, clone_rsc_colocation_rh, clone_rsc_order_lh, clone_rsc_order_rh, clone_rsc_location, clone_expand, complex_migrate_reload, complex_stonith_ordering, }, { native_merge_weights, master_color, master_create_actions, clone_create_probe, master_internal_constraints, clone_rsc_colocation_lh, master_rsc_colocation_rh, clone_rsc_order_lh, clone_rsc_order_rh, clone_rsc_location, clone_expand, complex_migrate_reload, complex_stonith_ordering, } }; 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 *op_version = 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); op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); 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, crm_strdup(key), RSC_CANCEL, active_node, FALSE, TRUE, data_set); crm_free(cancel->task); cancel->task = crm_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); crm_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; gboolean did_change = FALSE; gboolean start_op = FALSE; xmlNode *params_all = NULL; xmlNode *params_restart = NULL; GHashTable *local_rsc_params = NULL; char *digest_all_calc = NULL; const char *digest_all = NULL; const char *restart_list = NULL; const char *digest_restart = NULL; char *digest_restart_calc = NULL; action_t *action = NULL; const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK); const char *op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION); CRM_CHECK(active_node != NULL, return FALSE); 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); if(interval > 0) { xmlNode *op_match = NULL; crm_debug_2("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); return TRUE; } else if(op_match == NULL) { crm_debug("Orphan action detected: %s on %s", key, active_node->details->uname); crm_free(key); key = NULL; return TRUE; } } action = custom_action(rsc, key, task, active_node, TRUE, FALSE, data_set); local_rsc_params = g_hash_table_new_full( g_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); get_rsc_attributes(local_rsc_params, rsc, active_node, data_set); params_all = create_xml_node(NULL, XML_TAG_PARAMS); g_hash_table_foreach(local_rsc_params, hash2field, params_all); g_hash_table_foreach(action->extra, hash2field, params_all); g_hash_table_foreach(rsc->parameters, hash2field, params_all); g_hash_table_foreach(action->meta, hash2metafield, params_all); filter_action_parameters(params_all, op_version); digest_all_calc = calculate_xml_digest(params_all, TRUE, FALSE); digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST); digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST); restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART); if(crm_str_eq(task, RSC_START, TRUE)) { start_op = TRUE; } if(start_op && digest_restart) { params_restart = copy_xml(params_all); if(restart_list) { filter_reload_parameters(params_restart, restart_list); } digest_restart_calc = calculate_xml_digest(params_restart, TRUE, FALSE); if(safe_str_neq(digest_restart_calc, digest_restart)) { did_change = TRUE; crm_log_xml_info(params_restart, "params:restart"); crm_warn("Parameters to %s on %s changed: recorded %s vs. %s (restart:%s) %s", key, active_node->details->uname, crm_str(digest_restart), digest_restart_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); key = generate_op_key(rsc->id, task, interval); custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); goto cleanup; } } if(safe_str_neq(digest_all_calc, digest_all)) { action_t *op = NULL; did_change = TRUE; crm_log_xml_info(params_all, "params:all"); crm_warn("Parameters to %s on %s changed: recorded %s vs. %s (all:%s) %s", key, active_node->details->uname, crm_str(digest_all), digest_all_calc, op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC)); if(interval == 0 && safe_str_neq(task, RSC_STOP)) { /* Anything except stop actions should result in a restart, * never a re-probe */ task = RSC_START; } key = generate_op_key(rsc->id, task, interval); op = custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set); if(start_op && digest_restart) { op->allow_reload_conversion = TRUE; } else if(interval > 0) { custom_action_order(rsc, start_key(rsc), NULL, NULL, crm_strdup(op->task), op, pe_order_runnable_left, data_set); } } cleanup: free_xml(params_all); free_xml(params_restart); crm_free(digest_all_calc); crm_free(digest_restart_calc); g_hash_table_destroy(local_rsc_params); pe_free_action(action); 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) { int interval = 0; int stop_index = 0; int start_index = 0; const char *id = NULL; const char *task = NULL; const char *interval_s = NULL; GListPtr op_list = NULL; GListPtr sorted_op_list = NULL; gboolean is_probe = FALSE; CRM_CHECK(node != NULL, return); if(is_set(rsc->flags, pe_rsc_orphan)) { crm_debug_2("Skipping param check for %s: orphan", rsc->id); return; } else if(pe_find_node_id(rsc->running_on, node->details->id) == NULL) { crm_debug_2("Skipping param check for %s: no longer active on %s", rsc->id, node->details->uname); return; } crm_debug_3("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); } xml_child_iter_filter( rsc_entry, rsc_op, XML_LRM_TAG_RSC_OP, op_list = g_list_append(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); slist_iter( rsc_op, xmlNode, sorted_op_list, lpc, if(start_index < stop_index) { /* stopped */ continue; } else if(lpc < start_index) { /* action occurred prior to a start */ continue; } id = ID(rsc_op); is_probe = 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) { check_action_definition(rsc, node, rsc_op, data_set); } ); 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) { gboolean match = FALSE; if(id == NULL) { return NULL; } else if(rsc == NULL && data_set) { slist_iter(child, resource_t, data_set->resources, lpc, result = find_rsc_list(result, child, id, renamed_clones, partial, NULL)); return result; } if(partial) { if(strstr(rsc->id, id)) { match = TRUE; } else if(rsc->long_name && strstr(rsc->long_name, 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(rsc->long_name && strcmp(rsc->long_name, id) == 0) { match = TRUE; } else if(renamed_clones && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) { match = TRUE; } } if(match) { result = g_list_append(result, rsc); } if(rsc->children) { slist_iter(child, resource_t, rsc->children, lpc, 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); xml_child_iter_filter( status, node_state, XML_CIB_TAG_STATE, 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_debug_2("Skipping param check for %s: cant run resources", node->details->uname); continue; } crm_debug_2("Processing node %s", node->details->uname); if(node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) { xml_child_iter_filter( lrm_rscs, rsc_entry, XML_LRM_TAG_RESOURCE, if(xml_has_children(rsc_entry)) { 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); slist_iter(rsc, resource_t, result, lpc, check_actions_for(rsc_entry, rsc, node, data_set)); g_list_free(result); } ); } ); } static gboolean apply_placement_constraints(pe_working_set_t *data_set) { crm_debug_3("Applying constraints..."); slist_iter( cons, rsc_to_node_t, data_set->placement_constraints, lpc, cons->rsc_lh->cmds->rsc_location(cons->rsc_lh, cons); ); return TRUE; } 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) { slist_iter( child_rsc, resource_t, rsc->children, lpc, 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_find_node_id(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); crm_debug("Resource %s: preferring current location" " (node=%s, weight=%d)", sticky_rsc->id, node->details->uname, rsc->stickiness); } else { crm_debug("Ignoring stickiness for %s: the cluster is asymmetric" " and node %s is not explicitly allowed", rsc->id, node->details->uname); slist_iter(node, node_t, rsc->allowed_nodes, lpc, crm_err("%s[%s] = %d", rsc->id, node->details->uname, node->weight)); } } if(is_not_set(rsc->flags, pe_rsc_unique)) { failed = uber_parent(rsc); } fail_count = get_failcount(node, rsc, NULL, data_set); if(fail_count > 0 && rsc->migration_threshold != 0) { 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_notice("%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) { rsc->cmds = &resource_class_alloc_functions[rsc->variant]; slist_iter( child_rsc, resource_t, rsc->children, lpc, complex_set_cmds(child_rsc); ); } void set_alloc_actions(pe_working_set_t *data_set) { slist_iter( rsc, resource_t, data_set->resources, lpc, 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) { 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); slist_iter( node, node_t, data_set->nodes, lpc, int system_health = 0; /* 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) { slist_iter( rsc, resource_t, data_set->resources, lpc, 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; } cluster_status(data_set); set_alloc_actions(data_set); apply_system_health(data_set); unpack_constraints(cib_constraints, data_set); return TRUE; } /* * 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; slist_iter( node, node_t, data_set->nodes, lpc, gboolean force_probe = FALSE; 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) { force_probe = TRUE; } probe_node_complete = custom_action( NULL, crm_strdup(CRM_OP_PROBED), CRM_OP_PROBED, node, FALSE, TRUE, data_set); probe_node_complete->optional = crm_is_true(probed); probe_node_complete->priority = INFINITY; add_hash_param(probe_node_complete->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE); if(node->details->pending) { probe_node_complete->runnable = FALSE; 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); slist_iter( rsc, resource_t, data_set->resources, lpc2, if(rsc->cmds->create_probe( rsc, node, probe_node_complete, force_probe, data_set)) { probe_complete->optional = FALSE; probe_node_complete->optional = FALSE; custom_action_order( NULL, NULL, probe_complete, rsc, start_key(rsc), NULL, pe_order_optional, data_set); custom_action_order( NULL, NULL, probe_complete, rsc, stop_key(rsc), NULL, pe_order_optional, 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) { crm_debug_3("Applying placement constraints"); slist_iter( node, node_t, data_set->nodes, lpc, 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); slist_iter(node, node_t, data_set->nodes, lpc, slist_iter( rsc, resource_t, data_set->resources, lpc2, common_apply_stickiness(rsc, node, data_set); ); ); return TRUE; } /* * Create internal resource constraints before allocation */ gboolean stage3(pe_working_set_t *data_set) { slist_iter( rsc, resource_t, data_set->resources, lpc, 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; } gboolean stage5(pe_working_set_t *data_set) { + slist_iter( + node, node_t, data_set->nodes, lpc, + dump_node_capacity(show_utilization?0:utilization_log_level, "Original", node); + ); + /* Take (next) highest resource, assign it and create its actions */ slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->color(rsc, data_set); ); + slist_iter( + node, node_t, data_set->nodes, lpc, + dump_node_capacity(show_utilization?0:utilization_log_level, "Remaining", node); + ); + probe_resources(data_set); slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->create_actions(rsc, data_set); ); return TRUE; } static gboolean is_managed(const resource_t *rsc) { if(is_set(rsc->flags, pe_rsc_managed)) { return TRUE; } slist_iter( child_rsc, resource_t, rsc->children, lpc, if(is_managed(child_rsc)) { return TRUE; } ); return FALSE; } static gboolean any_managed_resouces(pe_working_set_t *data_set) { slist_iter( rsc, resource_t, data_set->resources, lpc, if(is_managed(rsc)) { return TRUE; } ); return FALSE; } /* * Create dependancies for stonith and shutdown operations */ gboolean stage6(pe_working_set_t *data_set) { action_t *dc_down = NULL; action_t *stonith_op = NULL; action_t *last_stonith = NULL; gboolean integrity_lost = FALSE; action_t *ready = get_pseudo_op(STONITH_UP, data_set); 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; crm_debug_3("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_info("Delaying fencing operations until there are resources to manage"); need_stonith = FALSE; } slist_iter( node, node_t, data_set->nodes, lpc, stonith_op = NULL; if(node->details->unclean && need_stonith) { pe_warn("Scheduling Node %s for STONITH", node->details->uname); stonith_op = custom_action( NULL, crm_strdup(CRM_OP_FENCE), 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", data_set->stonith_action); stonith_constraints(node, stonith_op, data_set); order_actions(ready, stonith_op, pe_order_implies_left); order_actions(stonith_op, all_stopped, pe_order_implies_right); if(node->details->is_dc) { dc_down = stonith_op; } else { if(last_stonith) { order_actions(last_stonith, stonith_op, pe_order_implies_left); } last_stonith = stonith_op; } } else if(node->details->online && node->details->shutdown) { action_t *down_op = NULL; crm_info("Scheduling Node %s for shutdown", node->details->uname); down_op = custom_action( NULL, crm_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_debug_2("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); slist_iter( node_stop, action_t, shutdown_matches, lpc, 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_implies_left); ); if(last_stonith && dc_down != last_stonith) { order_actions(last_stonith, dc_down, pe_order_implies_left); } g_list_free(shutdown_matches); } if(last_stonith) { order_actions(last_stonith, done, pe_order_implies_right); } order_actions(ready, done, pe_order_optional); 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 * */ gboolean stage7(pe_working_set_t *data_set) { crm_debug_4("Applying ordering constraints"); slist_iter( order, order_constraint_t, data_set->ordering_constraints, lpc, resource_t *rsc = order->lh_rsc; crm_debug_3("Applying ordering constraint: %d", order->id); if(rsc != NULL) { crm_debug_4("rsc_action-to-*"); rsc->cmds->rsc_order_lh(rsc, order, data_set); continue; } rsc = order->rh_rsc; if(rsc != NULL) { crm_debug_4("action-to-rsc_action"); rsc->cmds->rsc_order_rh(order->lh_action, rsc, order); } else { crm_debug_4("action-to-action"); order_actions( order->lh_action, order->rh_action, order->type); } ); /* This code may need optimization: * * ptest -x with 800 resources, 80 nodes, calling update_action_states() with: * no probes O(8s) * full probe detection but no creation O(2m) * full probe detection and creation O(Lifetime of the universe) * * Duplicate detection in order_actions() has resolved the issue for now, * the full detection with "creation" and "no creation" cases are now identical */ update_action_states(data_set->actions); slist_iter( rsc, resource_t, data_set->resources, lpc, rsc->cmds->migrate_reload(rsc, data_set); LogActions(rsc, data_set); ); 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) { const char *uname = NULL; const char *rsc_id = NULL; const char *last_rsc_id = NULL; if(list == NULL) { *rsc_list = crm_strdup(" "); if(node_list) { *node_list = crm_strdup(" "); } return; } *rsc_list = NULL; if(node_list) { *node_list = NULL; } slist_iter(entry, notify_entry_t, list, lpc, 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_debug_5("Adding %s (%dc) at offset %d", rsc_id, len-2, existing_len); crm_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_debug_5("Adding %s (%dc) at offset %d", uname, len-2, existing_len); crm_realloc(*node_list, len + existing_len); sprintf(*node_list + existing_len, "%s ", uname); } ); } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { g_hash_table_replace(user_data, crm_strdup(key), crm_strdup(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) { crm_debug_2("Op=%p confirm=%p", op, confirm); return NULL; } CRM_CHECK(node != NULL, return NULL); if(node->details->online == FALSE) { crm_debug_2("Skipping notification for %s: node offline", rsc->id); return NULL; } else if(op->runnable == FALSE) { crm_debug_2("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"); crm_debug_2("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, op->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 */ crm_debug_3("Ordering %s before %s (%d->%d)", op->uuid, trigger->uuid, trigger->id, op->id); order_actions(op, trigger, pe_order_implies_left); order_actions(trigger, confirm, pe_order_implies_left); 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) { slist_iter( mon, action_t, rsc->actions, lpc, const char *interval = g_hash_table_lookup(mon->meta, "interval"); if(interval == NULL || safe_str_eq(interval, "0")) { crm_debug_3("Skipping %s: interval", mon->uuid); continue; } else if(safe_str_eq(mon->task, "cancel")) { crm_debug_3("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; } crm_malloc0(n_data, sizeof(notify_data_t)); n_data->action = action; n_data->keys = g_hash_table_new_full( g_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, start->optional, TRUE, data_set); n_data->pre->pseudo = TRUE; n_data->pre->runnable = TRUE; add_hash_param(n_data->pre->meta, "notify_type", "pre"); add_hash_param(n_data->pre->meta, "notify_operation", n_data->action); /* 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, start->optional, TRUE, data_set); n_data->pre_done->pseudo = TRUE; n_data->pre_done->runnable = TRUE; add_hash_param(n_data->pre_done->meta, "notify_type", "pre"); add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action); 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, end->optional, TRUE, data_set); n_data->post->pseudo = TRUE; n_data->post->runnable = TRUE; n_data->post->priority = INFINITY; n_data->post->runnable = end->runnable; add_hash_param(n_data->post->meta, "notify_type", "post"); add_hash_param(n_data->post->meta, "notify_operation", n_data->action); /* 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, end->optional, TRUE, data_set); n_data->post_done->pseudo = TRUE; n_data->post_done->runnable = TRUE; n_data->post_done->priority = INFINITY; n_data->post_done->runnable = end->runnable; add_hash_param(n_data->post_done->meta, "notify_type", "pre"); add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action); order_actions(end, n_data->post, pe_order_implies_right); order_actions(n_data->post, n_data->post_done, pe_order_implies_right); } 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) { slist_iter(child, resource_t, rsc->children, lpc, collect_notification_data(child, state, activity, n_data); ); return; } if(state) { notify_entry_t *entry = NULL; crm_malloc0(entry, sizeof(notify_entry_t)); entry->rsc = rsc; if(rsc->running_on) { /* we only take the first one */ entry->node = rsc->running_on->data; } crm_debug_2("%s state: %s", rsc->id, role2text(rsc->role)); switch(rsc->role) { case RSC_ROLE_STOPPED: n_data->inactive = g_list_append(n_data->inactive, entry); break; case RSC_ROLE_STARTED: n_data->active = g_list_append(n_data->active, entry); break; case RSC_ROLE_SLAVE: n_data->slave = g_list_append(n_data->slave, entry); break; case RSC_ROLE_MASTER: n_data->master = g_list_append(n_data->master, entry); break; default: crm_err("Unsupported notify role"); crm_free(entry); break; } } if(activity) { notify_entry_t *entry = NULL; enum action_tasks task; slist_iter( op, action_t, rsc->actions, lpc, if(op->optional == FALSE && op->node != NULL) { crm_malloc0(entry, 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_append(n_data->start, entry); break; case stop_rsc: n_data->stop = g_list_append(n_data->stop, entry); break; case action_promote: n_data->promote = g_list_append(n_data->promote, entry); break; case action_demote: n_data->demote = g_list_append(n_data->demote, entry); break; default: crm_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, crm_strdup("notify_stop_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_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, crm_strdup("notify_start_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_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, crm_strdup("notify_demote_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_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, crm_strdup("notify_promote_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_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, crm_strdup("notify_active_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_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, crm_strdup("notify_slave_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_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, crm_strdup("notify_master_resource"), rsc_list); g_hash_table_insert(n_data->keys, crm_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, crm_strdup("notify_inactive_resource"), rsc_list); if(required && n_data->pre) { n_data->pre->optional = FALSE; n_data->pre_done->optional = FALSE; } if(required && n_data->post) { n_data->post->optional = FALSE; n_data->post_done->optional = FALSE; } return required; } void create_notifications(resource_t *rsc, notify_data_t *n_data, pe_working_set_t *data_set) { action_t *stop = NULL; action_t *start = NULL; enum action_tasks task = text2task(n_data->action); if(rsc->children) { slist_iter( child, resource_t, rsc->children, lpc, create_notifications(child, n_data, data_set); ); return; } /* Copy notification details into standard ops */ slist_iter( op, action_t, rsc->actions, lpc, if(op->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; } } ); crm_debug_2("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) { slist_iter(current_node, node_t, rsc->running_on, lpc, pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set); if(task == action_demote || stop == NULL || stop->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 || start->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; } pe_free_shallow(n_data->stop); pe_free_shallow(n_data->start); pe_free_shallow(n_data->demote); pe_free_shallow(n_data->promote); pe_free_shallow(n_data->master); pe_free_shallow(n_data->slave); pe_free_shallow(n_data->active); pe_free_shallow(n_data->inactive); g_hash_table_destroy(n_data->keys); crm_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) { const char *value = NULL; transition_id++; crm_debug_2("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); /* errors... slist_iter(action, action_t, action_list, lpc, if(action->optional == FALSE && action->runnable == FALSE) { print_action("Ignoring", action, TRUE); } ); */ slist_iter( rsc, resource_t, data_set->resources, lpc, crm_debug_4("processing actions for rsc=%s", rsc->id); rsc->cmds->expand(rsc, data_set); ); crm_log_xml_debug_3( data_set->graph, "created resource-driven action list"); /* catch any non-resource specific actions */ crm_debug_4("processing non-resource actions"); slist_iter( action, action_t, data_set->actions, lpc, graph_element_from_action(action, data_set); ); crm_log_xml_debug_3(data_set->graph, "created generic action list"); crm_debug_2("Created transition graph %d.", transition_id); return TRUE; } void cleanup_alloc_calculations(pe_working_set_t *data_set) { if(data_set == NULL) { return; } crm_debug_3("deleting order cons: %p", data_set->ordering_constraints); pe_free_ordering(data_set->ordering_constraints); data_set->ordering_constraints = NULL; crm_debug_3("deleting node cons: %p", data_set->placement_constraints); pe_free_rsc_to_node(data_set->placement_constraints); data_set->placement_constraints = NULL; crm_debug_3("deleting inter-resource cons: %p", data_set->colocation_constraints); pe_free_shallow(data_set->colocation_constraints); data_set->colocation_constraints = NULL; cleanup_calculations(data_set); } diff --git a/pengine/pengine.c b/pengine/pengine.c index 66d6c50030..a9e408fbd7 100644 --- a/pengine/pengine.c +++ b/pengine/pengine.c @@ -1,330 +1,332 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include xmlNode * do_calculations( pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); gboolean show_scores = FALSE; int scores_log_level = LOG_DEBUG_2; +gboolean show_utilization = FALSE; +int utilization_log_level = LOG_DEBUG_2; extern int transition_id; #define get_series() was_processing_error?1:was_processing_warning?2:3 typedef struct series_s { int id; const char *name; const char *param; int wrap; } series_t; series_t series[] = { { 0, "pe-unknown", "_dont_match_anything_", -1 }, { 0, "pe-error", "pe-error-series-max", -1 }, { 0, "pe-warn", "pe-warn-series-max", 200 }, { 0, "pe-input", "pe-input-series-max", 400 }, }; gboolean process_pe_message(xmlNode *msg, xmlNode *xml_data, IPC_Channel *sender) { gboolean send_via_disk = FALSE; const char *sys_to = crm_element_value(msg, F_CRM_SYS_TO); const char *op = crm_element_value(msg, F_CRM_TASK); const char *ref = crm_element_value(msg, XML_ATTR_REFERENCE); crm_debug_3("Processing %s op (ref=%s)...", op, ref); if(op == NULL){ /* error */ } else if(strcasecmp(op, CRM_OP_HELLO) == 0) { /* ignore */ } else if(safe_str_eq(crm_element_value(msg, F_CRM_MSG_TYPE), XML_ATTR_RESPONSE)) { /* ignore */ } else if(sys_to == NULL || strcasecmp(sys_to, CRM_SYSTEM_PENGINE) != 0) { crm_debug_3("Bad sys-to %s", crm_str(sys_to)); return FALSE; } else if(strcasecmp(op, CRM_OP_PECALC) == 0) { int seq = -1; int series_id = 0; int series_wrap = 0; char *filename = NULL; char *graph_file = NULL; const char *value = NULL; pe_working_set_t data_set; xmlNode *converted = NULL; xmlNode *reply = NULL; gboolean process = TRUE; #if HAVE_BZLIB_H gboolean compress = TRUE; #else gboolean compress = FALSE; #endif crm_config_error = FALSE; crm_config_warning = FALSE; was_processing_error = FALSE; was_processing_warning = FALSE; graph_file = crm_strdup(CRM_STATE_DIR"/graph.XXXXXX"); graph_file = mktemp(graph_file); converted = copy_xml(xml_data); if(cli_config_update(&converted, NULL, TRUE) == FALSE) { set_working_set_defaults(&data_set); data_set.graph = create_xml_node(NULL, XML_TAG_GRAPH); crm_xml_add_int(data_set.graph, "transition_id", 0); crm_xml_add_int(data_set.graph, "cluster-delay", 0); process = FALSE; } if(process) { do_calculations(&data_set, converted, NULL); } series_id = get_series(); series_wrap = series[series_id].wrap; value = pe_pref(data_set.config_hash, series[series_id].param); if(value != NULL) { series_wrap = crm_int_helper(value, NULL); if(errno != 0) { series_wrap = series[series_id].wrap; } } else { crm_config_warn("No value specified for cluster" " preference: %s", series[series_id].param); } seq = get_last_sequence(PE_STATE_DIR, series[series_id].name); data_set.input = NULL; reply = create_reply(msg, data_set.graph); CRM_ASSERT(reply != NULL); filename = generate_series_filename( PE_STATE_DIR, series[series_id].name, seq, compress); crm_xml_add(reply, F_CRM_TGRAPH_INPUT, filename); crm_xml_add_int(reply, "graph-errors", was_processing_error); crm_xml_add_int(reply, "graph-warnings", was_processing_warning); crm_xml_add_int(reply, "config-errors", crm_config_error); crm_xml_add_int(reply, "config-warnings", crm_config_warning); if(send_ipc_message(sender, reply) == FALSE) { if(sender && sender->ops->get_chan_status(sender) == IPC_CONNECT) { send_via_disk = TRUE; crm_err("Answer could not be sent via IPC, send via the disk instead"); crm_info("Writing the TE graph to %s", graph_file); if(write_xml_file(data_set.graph, graph_file, FALSE) < 0) { crm_err("TE graph could not be written to disk"); } } else { crm_info("Peer disconnected, discarding transition graph"); } } free_xml(reply); cleanup_alloc_calculations(&data_set); if(series_wrap != 0) { write_xml_file(xml_data, filename, compress); write_last_sequence(PE_STATE_DIR, series[series_id].name, seq+1, series_wrap); } if(was_processing_error) { crm_err("Transition %d:" " ERRORs found during PE processing." " PEngine Input stored in: %s", transition_id, filename); } else if(was_processing_warning) { crm_warn("Transition %d:" " WARNINGs found during PE processing." " PEngine Input stored in: %s", transition_id, filename); } else { crm_info("Transition %d: PEngine Input stored in: %s", transition_id, filename); } if(crm_config_error) { crm_info("Configuration ERRORs found during PE processing." " Please run \"crm_verify -L\" to identify issues."); } else if(crm_config_warning) { crm_info("Configuration WARNINGs found during PE processing." " Please run \"crm_verify -L\" to identify issues."); } if(send_via_disk) { reply = create_reply(msg, NULL); crm_xml_add(reply, F_CRM_TGRAPH, graph_file); crm_xml_add(reply, F_CRM_TGRAPH_INPUT, filename); CRM_ASSERT(reply != NULL); if(send_ipc_message(sender, reply) == FALSE) { crm_err("Answer could not be sent"); } free_xml(reply); } free_xml(converted); crm_free(graph_file); crm_free(filename); } else if(strcasecmp(op, CRM_OP_QUIT) == 0) { crm_warn("Received quit message, terminating"); exit(0); } return TRUE; } #define MEMCHECK_STAGE_0 0 #define check_and_exit(stage) cleanup_calculations(data_set); \ crm_mem_stats(NULL); \ crm_err("Exiting: stage %d", stage); \ exit(1); xmlNode * do_calculations(pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now) { int rsc_log_level = LOG_NOTICE; /* pe_debug_on(); */ set_working_set_defaults(data_set); data_set->input = xml_input; data_set->now = now; if(data_set->now == NULL) { data_set->now = new_ha_date(TRUE); } #if MEMCHECK_STAGE_SETUP check_and_exit(-1); #endif crm_debug_5("unpack constraints"); stage0(data_set); #if MEMCHECK_STAGE_0 check_and_exit(0); #endif slist_iter(rsc, resource_t, data_set->resources, lpc, if(is_set(rsc->flags, pe_rsc_orphan) && rsc->role == RSC_ROLE_STOPPED) { continue; } rsc->fns->print(rsc, NULL, pe_print_log, &rsc_log_level); ); #if MEMCHECK_STAGE_1 check_and_exit(1); #endif crm_debug_5("color resources"); stage2(data_set); #if MEMCHECK_STAGE_2 check_and_exit(2); #endif /* unused */ stage3(data_set); #if MEMCHECK_STAGE_3 check_and_exit(3); #endif crm_debug_5("assign nodes to colors"); stage4(data_set); #if MEMCHECK_STAGE_4 check_and_exit(4); #endif crm_debug_5("creating actions and internal ording constraints"); stage5(data_set); #if MEMCHECK_STAGE_5 check_and_exit(5); #endif crm_debug_5("processing fencing and shutdown cases"); stage6(data_set); #if MEMCHECK_STAGE_6 check_and_exit(6); #endif crm_debug_5("applying ordering constraints"); stage7(data_set); #if MEMCHECK_STAGE_7 check_and_exit(7); #endif crm_debug_5("creating transition graph"); stage8(data_set); #if MEMCHECK_STAGE_8 check_and_exit(8); #endif crm_debug_2("=#=#=#=#= Summary =#=#=#=#="); crm_debug_2("\t========= Set %d (Un-runnable) =========", -1); if(crm_log_level > LOG_DEBUG) { slist_iter(action, action_t, data_set->actions, lpc, if(action->optional == FALSE && action->runnable == FALSE && action->pseudo == FALSE) { log_action(LOG_DEBUG_2, "\t", action, TRUE); } ); } return data_set->graph; } diff --git a/pengine/pengine.h b/pengine/pengine.h index cb399e7a86..f625313d03 100644 --- a/pengine/pengine.h +++ b/pengine/pengine.h @@ -1,174 +1,176 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef PENGINE__H #define PENGINE__H typedef struct rsc_to_node_s rsc_to_node_t; typedef struct rsc_colocation_s rsc_colocation_t; typedef struct lrm_agent_s lrm_agent_t; typedef struct order_constraint_s order_constraint_t; #include #include #include #include #include #include #include #include enum pe_stop_fail { pesf_block, pesf_stonith, pesf_ignore }; enum pe_ordering { pe_order_none = 0x0, /* deleted */ pe_order_implies_left = 0x01, /* was: _mandatory */ pe_order_implies_right = 0x02, /* was: _recover */ pe_order_runnable_left = 0x10, /* needs the LHS side to be runnable */ pe_order_runnable_right = 0x20, /* needs the RHS side to be runnable */ pe_order_optional = 0x100, /* pure ordering, nothing implied */ pe_order_stonith_stop = 0x200, /* only applies if the action is non-pseudo */ pe_order_restart = 0x400, /* stop-start constraint */ pe_order_demote = 0x800, /* stop-start constraint */ pe_order_shutdown = 0x1000, /* combines with pe_order_restart to make a complex resource shut down */ pe_order_demote_stop = 0x2000, /* upgrades to implies_left if the resource is a master */ pe_order_complex_left = 0x10000, /* upgrades to implies left */ pe_order_complex_right = 0x20000, /* upgrades to implies right */ pe_order_implies_left_printed = 0x40000, /* Like implies left but only ensures the action is printed, not manditory */ pe_order_implies_right_printed = 0x80000, /* Like implies right but only ensures the action is printed, not manditory */ pe_order_serialize_only = 0x100000, /* serialize */ pe_order_test = 0x200000 /* test marker */ }; struct rsc_colocation_s { const char *id; const char *node_attribute; resource_t *rsc_lh; resource_t *rsc_rh; int role_lh; int role_rh; int score; }; struct rsc_to_node_s { const char *id; resource_t *rsc_lh; enum rsc_role_e role_filter; GListPtr node_list_rh; /* node_t* */ }; struct order_constraint_s { int id; enum pe_ordering type; void *lh_opaque; resource_t *lh_rsc; action_t *lh_action; char *lh_action_task; void *rh_opaque; resource_t *rh_rsc; action_t *rh_action; char *rh_action_task; /* (soon to be) variant specific */ /* int lh_rsc_incarnation; */ /* int rh_rsc_incarnation; */ }; enum pe_link_state { pe_link_not_dumped, pe_link_dumped, pe_link_dup, }; typedef struct action_wrapper_s action_wrapper_t; struct action_wrapper_s { enum pe_ordering type; enum pe_link_state state; action_t *action; }; extern gboolean stage0(pe_working_set_t *data_set); extern gboolean probe_resources(pe_working_set_t *data_set); extern gboolean stage2(pe_working_set_t *data_set); extern gboolean stage3(pe_working_set_t *data_set); extern gboolean stage4(pe_working_set_t *data_set); extern gboolean stage5(pe_working_set_t *data_set); extern gboolean stage6(pe_working_set_t *data_set); extern gboolean stage7(pe_working_set_t *data_set); extern gboolean stage8(pe_working_set_t *data_set); extern gboolean summary(GListPtr resources); extern gboolean pe_msg_dispatch(IPC_Channel *sender, void *user_data); extern gboolean process_pe_message( xmlNode *msg, xmlNode *xml_data, IPC_Channel *sender); extern gboolean unpack_constraints( xmlNode *xml_constraints, pe_working_set_t *data_set); extern gboolean update_action_states(GListPtr actions); extern gboolean shutdown_constraints( node_t *node, action_t *shutdown_op, pe_working_set_t *data_set); extern gboolean stonith_constraints( node_t *node, action_t *stonith_op, pe_working_set_t *data_set); extern int custom_action_order( resource_t *lh_rsc, char *lh_task, action_t *lh_action, resource_t *rh_rsc, char *rh_task, action_t *rh_action, enum pe_ordering type, pe_working_set_t *data_set); extern int new_rsc_order(resource_t *lh_rsc, const char *lh_task, resource_t *rh_rsc, const char *rh_task, enum pe_ordering type, pe_working_set_t *data_set); #define order_start_start(rsc1,rsc2, type) \ new_rsc_order(rsc1, CRMD_ACTION_START, rsc2, CRMD_ACTION_START, type, data_set) #define order_stop_stop(rsc1, rsc2, type) \ new_rsc_order(rsc1, CRMD_ACTION_STOP, rsc2, CRMD_ACTION_STOP, type, data_set) extern void graph_element_from_action( action_t *action, pe_working_set_t *data_set); extern gboolean show_scores; extern int scores_log_level; +extern gboolean show_utilization; +extern int utilization_log_level; extern const char* transition_idle_timeout; #endif diff --git a/pengine/ptest.c b/pengine/ptest.c index 7210130b62..4e5daaa40d 100644 --- a/pengine/ptest.c +++ b/pengine/ptest.c @@ -1,479 +1,487 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_LIBXML2 # include #endif gboolean use_stdin = FALSE; gboolean do_simulation = FALSE; gboolean inhibit_exit = FALSE; gboolean all_actions = FALSE; extern xmlNode * do_calculations( pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); extern void cleanup_calculations(pe_working_set_t *data_set); char *use_date = NULL; FILE *dot_strm = NULL; #define DOT_PREFIX "PE_DOT: " /* #define DOT_PREFIX "" */ #define dot_write(fmt...) if(dot_strm != NULL) { \ fprintf(dot_strm, fmt); \ fprintf(dot_strm, "\n"); \ } else { \ crm_debug(DOT_PREFIX""fmt); \ } static void init_dotfile(void) { dot_write(" digraph \"g\" {"); /* dot_write(" size = \"30,30\""); */ /* dot_write(" graph ["); */ /* dot_write(" fontsize = \"12\""); */ /* dot_write(" fontname = \"Times-Roman\""); */ /* dot_write(" fontcolor = \"black\""); */ /* dot_write(" bb = \"0,0,398.922306,478.927856\""); */ /* dot_write(" color = \"black\""); */ /* dot_write(" ]"); */ /* dot_write(" node ["); */ /* dot_write(" fontsize = \"12\""); */ /* dot_write(" fontname = \"Times-Roman\""); */ /* dot_write(" fontcolor = \"black\""); */ /* dot_write(" shape = \"ellipse\""); */ /* dot_write(" color = \"black\""); */ /* dot_write(" ]"); */ /* dot_write(" edge ["); */ /* dot_write(" fontsize = \"12\""); */ /* dot_write(" fontname = \"Times-Roman\""); */ /* dot_write(" fontcolor = \"black\""); */ /* dot_write(" color = \"black\""); */ /* dot_write(" ]"); */ } static char * create_action_name(action_t *action) { char *action_name = NULL; const char *action_host = NULL; if(action->node) { action_host = action->node->details->uname; action_name = crm_concat(action->uuid, action_host, ' '); } else if(action->pseudo) { action_name = crm_strdup(action->uuid); } else { action_host = ""; action_name = crm_concat(action->uuid, action_host, ' '); } if(safe_str_eq(action->task, RSC_CANCEL)) { char *tmp_action_name = action_name; action_name = crm_concat("Cancel", tmp_action_name, ' '); crm_free(tmp_action_name); } return action_name; } gboolean USE_LIVE_CIB = FALSE; static struct crm_option long_options[] = { /* Top-level Options */ {"help", 0, 0, '?', "This text"}, {"version", 0, 0, '$', "Version information" }, {"verbose", 0, 0, 'V', "Increase debug output\n"}, {"simulate", 0, 0, 'S', "Simulate the transition's execution to find invalid graphs\n"}, {"show-scores", 0, 0, 's', "Display resource allocation scores"}, + {"show-utilization", 0, 0, 'U', "Display utilization information"}, {"all-actions", 0, 0, 'a', "Display all possible actions - even ones not part of the transition graph"}, {"live-check", 0, 0, 'L', "Connect to the CIB and use the current contents as input"}, {"xml-text", 1, 0, 'X', "Retrieve XML from the supplied string"}, {"xml-file", 1, 0, 'x', "Retrieve XML from the named file"}, /* {"xml-pipe", 0, 0, 'p', "Retrieve XML from stdin\n"}, */ {"save-input", 1, 0, 'I', "\tSave the input 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\n"}, {0, 0, 0, 0} }; int main(int argc, char **argv) { gboolean process = TRUE; gboolean all_good = TRUE; enum transition_status graph_rc = -1; crm_graph_t *transition = NULL; ha_time_t *a_date = NULL; cib_t * cib_conn = NULL; xmlNode * cib_object = NULL; int argerr = 0; int flag; char *msg_buffer = NULL; gboolean optional = FALSE; pe_working_set_t data_set; const char *source = NULL; const char *xml_file = NULL; const char *dot_file = NULL; const char *graph_file = NULL; const char *input_file = NULL; /* disable glib's fancy allocators that can't be free'd */ GMemVTable vtable; vtable.malloc = malloc; vtable.realloc = realloc; vtable.free = free; vtable.calloc = calloc; vtable.try_malloc = malloc; vtable.try_realloc = realloc; g_mem_set_vtable(&vtable); crm_log_init("ptest", LOG_CRIT, FALSE, FALSE, 0, NULL); - crm_set_options("V?$XD:G:I:Lwx:d:aSs", "[-?Vv] -[Xxp] {other options}", long_options, + crm_set_options("V?$XD:G:I:Lwx:d:aSsU", "[-?Vv] -[Xxp] {other options}", long_options, "Calculate the cluster's response to the supplied cluster state\n"); while (1) { int option_index = 0; flag = crm_get_option(argc, argv, &option_index); if (flag == -1) break; switch(flag) { case 'S': do_simulation = TRUE; break; case 'a': all_actions = TRUE; break; case 'w': inhibit_exit = TRUE; break; case 'X': use_stdin = TRUE; break; case 's': show_scores = TRUE; break; + case 'U': + show_utilization = TRUE; + break; case 'x': xml_file = optarg; break; case 'd': use_date = optarg; break; case 'D': dot_file = optarg; break; case 'G': graph_file = optarg; break; case 'I': input_file = optarg; break; case 'V': cl_log_enable_stderr(TRUE); alter_debug(DEBUG_INC); break; case 'L': USE_LIVE_CIB = TRUE; break; case '$': case '?': crm_help(flag, 0); break; default: fprintf(stderr, "Option -%c is not yet supported\n", flag); ++argerr; break; } } if (optind < argc) { printf("non-option ARGV-elements: "); while (optind < argc) { printf("%s ", argv[optind++]); } printf("\n"); } if (optind > argc) { ++argerr; } if (argerr) { crm_err("%d errors in option parsing", argerr); crm_help('?', 1); } if(USE_LIVE_CIB) { int rc = cib_ok; source = "live cib"; cib_conn = cib_new(); rc = cib_conn->cmds->signon(cib_conn, "ptest", cib_command); if(rc == cib_ok) { crm_info("Reading XML from: live cluster"); cib_object = get_cib_copy(cib_conn); } else { fprintf(stderr, "Live CIB query failed: %s\n", cib_error2string(rc)); return 3; } if(cib_object == NULL) { fprintf(stderr, "Live CIB query failed: empty result\n"); return 3; } } else if(xml_file != NULL) { source = xml_file; cib_object = filename2xml(xml_file); } else if(use_stdin) { source = "stdin"; cib_object = filename2xml(NULL); } if(cib_object == NULL && source) { fprintf(stderr, "Could not parse configuration input from: %s\n", source); return 4; } else if(cib_object == NULL) { fprintf(stderr, "No configuration specified\n"); crm_help('?', 1); } if(cli_config_update(&cib_object, NULL, FALSE) == FALSE) { free_xml(cib_object); return cib_STALE; } if(validate_xml(cib_object, NULL, FALSE) != TRUE) { free_xml(cib_object); return cib_dtd_validation; } if(input_file != NULL) { FILE *input_strm = fopen(input_file, "w"); if(input_strm == NULL) { crm_perror(LOG_ERR,"Could not open %s for writing", input_file); } else { msg_buffer = dump_xml_formatted(cib_object); if(fprintf(input_strm, "%s\n", msg_buffer) < 0) { crm_perror(LOG_ERR,"Write to %s failed", input_file); } fflush(input_strm); fclose(input_strm); crm_free(msg_buffer); } } if(use_date != NULL) { a_date = parse_date(&use_date); log_date(LOG_WARNING, "Set fake 'now' to", a_date, ha_log_date|ha_log_time); log_date(LOG_WARNING, "Set fake 'now' to (localtime)", a_date, ha_log_date|ha_log_time|ha_log_local); } if(process) { - if(show_scores) { + if(show_scores && show_utilization) { + fprintf(stdout, "Allocation scores and utilization information:\n"); + } else if(show_scores) { fprintf(stdout, "Allocation scores:\n"); + } else if(show_utilization) { + fprintf(stdout, "Utilization information:\n"); } do_calculations(&data_set, cib_object, a_date); } msg_buffer = dump_xml_formatted(data_set.graph); if(safe_str_eq(graph_file, "-")) { fprintf(stdout, "%s\n", msg_buffer); fflush(stdout); } else if(graph_file != NULL) { FILE *graph_strm = fopen(graph_file, "w"); if(graph_strm == NULL) { crm_perror(LOG_ERR,"Could not open %s for writing", graph_file); } else { if(fprintf(graph_strm, "%s\n\n", msg_buffer) < 0) { crm_perror(LOG_ERR,"Write to %s failed", graph_file); } fflush(graph_strm); fclose(graph_strm); } } crm_free(msg_buffer); if(dot_file != NULL) { dot_strm = fopen(dot_file, "w"); if(dot_strm == NULL) { crm_perror(LOG_ERR,"Could not open %s for writing", dot_file); } } if(dot_strm == NULL) { goto simulate; } init_dotfile(); slist_iter( action, action_t, data_set.actions, lpc, const char *style = "filled"; const char *font = "black"; const char *color = "black"; const char *fill = NULL; char *action_name = create_action_name(action); crm_debug_3("Action %d: %p", action->id, action); if(action->pseudo) { font = "orange"; } style = "dashed"; if(action->dumped) { style = "bold"; color = "green"; } else if(action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) { color = "purple"; if(all_actions == FALSE) { goto dont_write; } } else if(action->optional) { color = "blue"; if(all_actions == FALSE) { goto dont_write; } } else { color = "red"; CRM_CHECK(action->runnable == FALSE, ;); } action->dumped = TRUE; dot_write("\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]", action_name, style, color, font, fill?"fillcolor=":"", fill?fill:""); dont_write: crm_free(action_name); ); slist_iter( action, action_t, data_set.actions, lpc, slist_iter( before, action_wrapper_t, action->actions_before, lpc2, char *before_name = NULL; char *after_name = NULL; const char *style = "dashed"; optional = TRUE; if(before->state == pe_link_dumped) { optional = FALSE; style = "bold"; } else if(action->pseudo && (before->type & pe_order_stonith_stop)) { continue; } else if(before->state == pe_link_dup) { continue; } else if(before->type == pe_order_none) { continue; } else if(action->dumped && before->action->dumped) { optional = FALSE; } if(all_actions || optional == FALSE) { before_name = create_action_name(before->action); after_name = create_action_name(action); dot_write("\"%s\" -> \"%s\" [ style = %s]", before_name, after_name, style); crm_free(before_name); crm_free(after_name); } ); ); dot_write("}"); if(dot_strm != NULL) { fflush(dot_strm); fclose(dot_strm); } simulate: if(do_simulation == FALSE) { goto cleanup; } transition = unpack_graph(data_set.graph, "ptest"); print_graph(LOG_DEBUG, transition); do { graph_rc = run_graph(transition); } while(graph_rc == transition_active); if(graph_rc != transition_complete) { crm_crit("Transition failed: %s", transition_status(graph_rc)); print_graph(LOG_ERR, transition); } destroy_graph(transition); CRM_CHECK(graph_rc == transition_complete, all_good = FALSE; crm_err("An invalid transition was produced")); cleanup: cleanup_alloc_calculations(&data_set); crm_log_deinit(); /* required for MallocDebug.app */ if(inhibit_exit) { GMainLoop* mainloop = g_main_new(FALSE); g_main_run(mainloop); } if(all_good) { return 0; } return graph_rc; } diff --git a/pengine/utils.c b/pengine/utils.c index f8834a238c..4cfff9c8ec 100644 --- a/pengine/utils.c +++ b/pengine/utils.c @@ -1,656 +1,660 @@ /* * Copyright (C) 2004 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include void print_rsc_to_node(const char *pre_text, rsc_to_node_t *cons, gboolean details) { if(cons == NULL) { crm_debug_4("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_4("%s%s%s Constraint %s (%p) - %d nodes:", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", "rsc_to_node", cons->id, cons, g_list_length(cons->node_list_rh)); if(details == FALSE) { crm_debug_4("\t%s (node placement rule)", safe_val3(NULL, cons, rsc_lh, id)); slist_iter( node, node_t, cons->node_list_rh, lpc, print_node("\t\t-->", node, FALSE) ); } } void print_rsc_colocation(const char *pre_text, rsc_colocation_t *cons, gboolean details) { if(cons == NULL) { crm_debug_4("%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } crm_debug_4("%s%s%s Constraint %s (%p):", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", XML_CONS_TAG_RSC_DEPEND, cons->id, cons); if(details == FALSE) { crm_debug_4("\t%s --> %s, %d", safe_val3(NULL, cons, rsc_lh, id), safe_val3(NULL, cons, rsc_rh, id), cons->score); } } void pe_free_ordering(GListPtr constraints) { GListPtr iterator = constraints; while(iterator != NULL) { order_constraint_t *order = iterator->data; iterator = iterator->next; crm_free(order->lh_action_task); crm_free(order->rh_action_task); crm_free(order); } if(constraints != NULL) { g_list_free(constraints); } } void pe_free_rsc_to_node(GListPtr constraints) { GListPtr iterator = constraints; while(iterator != NULL) { rsc_to_node_t *cons = iterator->data; iterator = iterator->next; pe_free_shallow(cons->node_list_rh); crm_free(cons); } if(constraints != NULL) { g_list_free(constraints); } } rsc_to_node_t * rsc2node_new(const char *id, resource_t *rsc, int node_weight, node_t *foo_node, pe_working_set_t *data_set) { rsc_to_node_t *new_con = NULL; if(rsc == NULL || id == NULL) { pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc); return NULL; } else if(foo_node == NULL) { CRM_CHECK(node_weight == 0, return NULL); } crm_malloc0(new_con, sizeof(rsc_to_node_t)); if(new_con != NULL) { new_con->id = id; new_con->rsc_lh = rsc; new_con->node_list_rh = NULL; new_con->role_filter = RSC_ROLE_UNKNOWN; if(foo_node != NULL) { node_t *copy = node_copy(foo_node); copy->weight = node_weight; new_con->node_list_rh = g_list_append(NULL, copy); } data_set->placement_constraints = g_list_append( data_set->placement_constraints, new_con); rsc->rsc_location = g_list_append(rsc->rsc_location, new_con); } return new_con; } const char * ordering_type2text(enum pe_ordering type) { const char *result = ""; if(type & pe_order_implies_left) { /* was: mandatory */ result = "right_implies_left"; } else if(type & pe_order_implies_right) { /* was: recover */ result = "left_implies_right"; } else if(type & pe_order_optional) { /* pure ordering, nothing implied */ result = "optional"; } else if(type & pe_order_runnable_left) { result = "runnable"; /* } else { */ /* crm_err("Unknown ordering type: %.3x", type); */ } return result; } gboolean can_run_resources(const node_t *node) { if(node == NULL) { return FALSE; } if(node->details->online == FALSE || node->details->shutdown || node->details->unclean || node->details->standby) { crm_debug_2("%s: online=%d, unclean=%d, standby=%d", node->details->uname, node->details->online, node->details->unclean, node->details->standby); return FALSE; } return TRUE; } struct compare_data { const node_t *node1; const node_t *node2; int result; }; static void do_compare_capacity1(gpointer key, gpointer value, gpointer user_data) { int node1_capacity = 0; int node2_capacity = 0; struct compare_data *data = user_data; node1_capacity = crm_parse_int(value, "0"); node2_capacity = crm_parse_int(g_hash_table_lookup(data->node2->details->utilization, key), "0"); if (node1_capacity > node2_capacity) { data->result--; } else if (node1_capacity < node2_capacity) { data->result++; } } static void do_compare_capacity2(gpointer key, gpointer value, gpointer user_data) { int node1_capacity = 0; int node2_capacity = 0; struct compare_data *data = user_data; if (g_hash_table_lookup_extended(data->node1->details->utilization, key, NULL, NULL)) { return; } node1_capacity = 0; node2_capacity = crm_parse_int(value, "0"); if (node1_capacity > node2_capacity) { data->result--; } else if (node1_capacity < node2_capacity) { data->result++; } } /* rc < 0 if 'node1' has more capacity remaining * rc > 0 if 'node1' has less capacity remaining */ static int compare_capacity(const node_t *node1, const node_t *node2) { struct compare_data data; data.node1 = node1; data.node2 = node2; data.result = 0; g_hash_table_foreach(node1->details->utilization, do_compare_capacity1, &data); g_hash_table_foreach(node2->details->utilization, do_compare_capacity2, &data); return data.result; } /* return -1 if 'a' is more preferred * return 1 if 'b' is more preferred */ gint sort_node_weight(gconstpointer a, gconstpointer b, gpointer data) { int level = LOG_DEBUG_3; const node_t *node1 = (const node_t*)a; const node_t *node2 = (const node_t*)b; const pe_working_set_t *data_set = (const pe_working_set_t*)data; int node1_weight = 0; int node2_weight = 0; int result = 0; if(a == NULL) { return 1; } if(b == NULL) { return -1; } node1_weight = node1->weight; node2_weight = node2->weight; if(can_run_resources(node1) == FALSE) { node1_weight = -INFINITY; } if(can_run_resources(node2) == FALSE) { node2_weight = -INFINITY; } if(node1_weight > node2_weight) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); return -1; } if(node1_weight < node2_weight) { do_crm_log_unlikely(level, "%s (%d) < %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); return 1; } do_crm_log_unlikely(level, "%s (%d) == %s (%d) : weight", node1->details->uname, node1_weight, node2->details->uname, node2_weight); if (safe_str_eq(data_set->placement_strategy, "minimal")) { goto equal; } if (safe_str_eq(data_set->placement_strategy, "balanced")) { result = compare_capacity(node1, node2); if (result != 0) { return result; } } /* now try to balance resources across the cluster */ if(node1->details->num_resources < node2->details->num_resources) { do_crm_log_unlikely(level, "%s (%d) < %s (%d) : resources", node1->details->uname, node1->details->num_resources, node2->details->uname, node2->details->num_resources); return -1; } else if(node1->details->num_resources > node2->details->num_resources) { do_crm_log_unlikely(level, "%s (%d) > %s (%d) : resources", node1->details->uname, node1->details->num_resources, node2->details->uname, node2->details->num_resources); return 1; } equal: do_crm_log_unlikely(level, "%s = %s", node1->details->uname, node2->details->uname); return 0; } struct calculate_data { - node_t *node; - gboolean allocate; + node_t *node; + gboolean allocate; }; static void do_calculate_utilization(gpointer key, gpointer value, gpointer user_data) { const char *capacity = NULL; char *remain_capacity = NULL; struct calculate_data *data = user_data; capacity = g_hash_table_lookup(data->node->details->utilization, key); if (capacity) { if (data->allocate) { remain_capacity = crm_itoa(crm_parse_int(capacity, "0") - crm_parse_int(value, "0")); } else { remain_capacity = crm_itoa(crm_parse_int(capacity, "0") + crm_parse_int(value, "0")); } g_hash_table_replace(data->node->details->utilization, crm_strdup(key), remain_capacity); } } /* Specify 'allocate' to TRUE when allocating * Otherwise to FALSE when deallocating */ static void calculate_utilization(node_t *node, resource_t *rsc, gboolean allocate) { struct calculate_data data; data.node = node; data.allocate = allocate; g_hash_table_foreach(rsc->utilization, do_calculate_utilization, &data); + + if (allocate) { + dump_rsc_utilization(show_utilization?0:utilization_log_level, __FUNCTION__, rsc, node); + } } gboolean native_assign_node(resource_t *rsc, GListPtr nodes, node_t *chosen, gboolean force) { CRM_ASSERT(rsc->variant == pe_native); clear_bit(rsc->flags, pe_rsc_provisional); if(chosen == NULL) { crm_debug("Could not allocate a node for %s", rsc->id); rsc->next_role = RSC_ROLE_STOPPED; return FALSE; } else if(force == FALSE && (can_run_resources(chosen) == FALSE || chosen->weight < 0)) { crm_debug("All nodes for resource %s are unavailable" ", unclean or shutting down (%s: %d, %d)", rsc->id, chosen->details->uname, can_run_resources(chosen), chosen->weight); rsc->next_role = RSC_ROLE_STOPPED; return FALSE; } /* todo: update the old node for each resource to reflect its * new resource count */ if(rsc->allocated_to) { node_t *old = rsc->allocated_to; old->details->allocated_rsc = g_list_remove( old->details->allocated_rsc, rsc); old->details->num_resources--; old->count--; calculate_utilization(old, rsc, FALSE); } crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id); crm_free(rsc->allocated_to); rsc->allocated_to = node_copy(chosen); chosen->details->allocated_rsc = g_list_append(chosen->details->allocated_rsc, rsc); chosen->details->num_resources++; chosen->count++; calculate_utilization(chosen, rsc, TRUE); return TRUE; } char * convert_non_atomic_task(char *old_uuid, resource_t *rsc, gboolean allow_notify, gboolean free_original) { int interval = 0; char *uuid = NULL; char *rid = NULL; char *raw_task = NULL; int task = no_action; crm_debug_3("Processing %s", old_uuid); if(old_uuid == NULL) { return NULL; } else if(strstr(old_uuid, "notify") != NULL) { goto done; /* no conversion */ } else if(rsc->variant < pe_group) { goto done; /* no conversion */ } CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval)); if(interval > 0) { goto done; /* no conversion */ } task = text2task(raw_task); switch(task) { case stop_rsc: case start_rsc: case action_notify: case action_promote: case action_demote: break; case stopped_rsc: case started_rsc: case action_notified: case action_promoted: case action_demoted: task--; break; case monitor_rsc: case shutdown_crm: case stonith_node: task = no_action; break; default: crm_err("Unknown action: %s", raw_task); task = no_action; break; } if(task != no_action) { if(is_set(rsc->flags, pe_rsc_notify) && allow_notify) { uuid = generate_notify_key(rid, "confirmed-post", task2text(task+1)); } else { uuid = generate_op_key(rid, task2text(task+1), 0); } crm_debug_2("Converted %s -> %s", old_uuid, uuid); } done: if(uuid == NULL) { uuid = crm_strdup(old_uuid); } if(free_original) { crm_free(old_uuid); } crm_free(raw_task); crm_free(rid); return uuid; } void order_actions( action_t *lh_action, action_t *rh_action, enum pe_ordering order) { action_wrapper_t *wrapper = NULL; GListPtr list = NULL; crm_debug_3("Ordering Action %s before %s", lh_action->uuid, rh_action->uuid); log_action(LOG_DEBUG_4, "LH (order_actions)", lh_action, FALSE); log_action(LOG_DEBUG_4, "RH (order_actions)", rh_action, FALSE); /* Filter dups, otherwise update_action_states() has too much work to do */ slist_iter(after, action_wrapper_t, lh_action->actions_after, lpc, if(after->action == rh_action && (after->type & order)) { return; } ); crm_malloc0(wrapper, sizeof(action_wrapper_t)); wrapper->action = rh_action; wrapper->type = order; list = lh_action->actions_after; list = g_list_append(list, wrapper); lh_action->actions_after = list; wrapper = NULL; /* order |= pe_order_implies_right; */ /* order ^= pe_order_implies_right; */ crm_malloc0(wrapper, sizeof(action_wrapper_t)); wrapper->action = lh_action; wrapper->type = order; list = rh_action->actions_before; list = g_list_append(list, wrapper); rh_action->actions_before = list; } void log_action(unsigned int log_level, const char *pre_text, action_t *action, gboolean details) { const char *node_uname = NULL; const char *node_uuid = NULL; if(action == NULL) { do_crm_log_unlikely(log_level, "%s%s: ", pre_text==NULL?"":pre_text, pre_text==NULL?"":": "); return; } if(action->pseudo) { node_uname = NULL; node_uuid = NULL; } else if(action->node != NULL) { node_uname = action->node->details->uname; node_uuid = action->node->details->id; } else { node_uname = ""; node_uuid = NULL; } switch(text2task(action->task)) { case stonith_node: case shutdown_crm: do_crm_log_unlikely(log_level, "%s%s%sAction %d: %s%s%s%s%s%s", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", action->pseudo?"Pseduo ":action->optional?"Optional ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ", action->id, action->uuid, node_uname?"\ton ":"", node_uname?node_uname:"", node_uuid?"\t\t(":"", node_uuid?node_uuid:"", node_uuid?")":""); break; default: do_crm_log_unlikely(log_level, "%s%s%sAction %d: %s %s%s%s%s%s%s", pre_text==NULL?"":pre_text, pre_text==NULL?"":": ", action->optional?"Optional ":action->pseudo?"Pseduo ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ", action->id, action->uuid, safe_val3("", action, rsc, id), node_uname?"\ton ":"", node_uname?node_uname:"", node_uuid?"\t\t(":"", node_uuid?node_uuid:"", node_uuid?")":""); break; } if(details) { do_crm_log_unlikely(log_level+1, "\t\t====== Preceding Actions"); slist_iter( other, action_wrapper_t, action->actions_before, lpc, log_action(log_level+1, "\t\t", other->action, FALSE); ); do_crm_log_unlikely(log_level+1, "\t\t====== Subsequent Actions"); slist_iter( other, action_wrapper_t, action->actions_after, lpc, log_action(log_level+1, "\t\t", other->action, FALSE); ); do_crm_log_unlikely(log_level+1, "\t\t====== End"); } else { do_crm_log_unlikely(log_level, "\t\t(seen=%d, before=%d, after=%d)", action->seen_count, g_list_length(action->actions_before), g_list_length(action->actions_after)); } } action_t *get_pseudo_op(const char *name, pe_working_set_t *data_set) { action_t *op = NULL; const char *op_s = name; GListPtr possible_matches = NULL; possible_matches = find_actions(data_set->actions, name, NULL); if(possible_matches != NULL) { if(g_list_length(possible_matches) > 1) { pe_warn("Action %s exists %d times", name, g_list_length(possible_matches)); } op = g_list_nth_data(possible_matches, 0); g_list_free(possible_matches); } else { op = custom_action(NULL, crm_strdup(op_s), op_s, NULL, TRUE, TRUE, data_set); op->pseudo = TRUE; op->runnable = TRUE; } return op; } gboolean can_run_any(GListPtr nodes) { if(nodes == NULL) { return FALSE; } slist_iter( node, node_t, nodes, lpc, if(can_run_resources(node) && node->weight >= 0) { return TRUE; } ); return FALSE; } diff --git a/tools/crm_inject.c b/tools/crm_inject.c index ae65d8d95f..9e811f6263 100644 --- a/tools/crm_inject.c +++ b/tools/crm_inject.c @@ -1,1088 +1,1097 @@ /* * Copyright (C) 2009 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include #include cib_t *global_cib = NULL; GListPtr op_fail = NULL; gboolean quiet = FALSE; #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 xmlNode * do_calculations( pe_working_set_t *data_set, xmlNode *xml_input, ha_time_t *now); static xmlNode *find_resource(xmlNode *cib_node, const char *resource) { char *xpath = NULL; xmlNode *match = NULL; const char *node = crm_element_value(cib_node, XML_ATTR_UNAME); int max = strlen(rsc_template) + strlen(resource) + strlen(node) + 1; crm_malloc0(xpath, max); snprintf(xpath, max, rsc_template, node, resource); match = get_xpath_object(xpath, cib_node, LOG_DEBUG_2); crm_free(xpath); return match; } static xmlNode *inject_node_state(cib_t *cib_conn, char *node) { int rc = cib_ok; int max = strlen(rsc_template) + strlen(node) + 1; char *xpath = NULL; xmlNode *cib_object = NULL; crm_malloc0(xpath, max); snprintf(xpath, max, node_template, node); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); if(rc == cib_NOTEXISTS) { char *uuid = NULL; cib_object = create_xml_node(NULL, XML_CIB_TAG_STATE); determine_host(cib_conn, &node, &uuid); crm_xml_add(cib_object, XML_ATTR_UUID, uuid); crm_xml_add(cib_object, XML_ATTR_UNAME, node); cib_conn->cmds->create(cib_conn, XML_CIB_TAG_STATUS, cib_object, cib_sync_call|cib_scope_local); rc = cib_conn->cmds->query(cib_conn, xpath, &cib_object, cib_xpath|cib_sync_call|cib_scope_local); } CRM_ASSERT(rc == cib_ok); return cib_object; } static xmlNode *modify_node(cib_t *cib_conn, char *node, gboolean up) { xmlNode *cib_node = inject_node_state(cib_conn, node); if(up) { crm_xml_add(cib_node, XML_CIB_ATTR_HASTATE, ACTIVESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_YES); crm_xml_add(cib_node, XML_CIB_ATTR_CRMDSTATE, ONLINESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_MEMBER); crm_xml_add(cib_node, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_MEMBER); } else { crm_xml_add(cib_node, XML_CIB_ATTR_HASTATE, DEADSTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO); crm_xml_add(cib_node, XML_CIB_ATTR_CRMDSTATE, OFFLINESTATUS); crm_xml_add(cib_node, XML_CIB_ATTR_JOINSTATE, CRMD_JOINSTATE_DOWN); crm_xml_add(cib_node, XML_CIB_ATTR_EXPSTATE, CRMD_JOINSTATE_DOWN); } crm_xml_add(cib_node, XML_ATTR_ORIGIN, crm_system_name); return cib_node; } static 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); crm_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; 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; } crm_info("Injecting new resource %s into %s '%s'", resource, xmlGetNodePath(cib_node), ID(cib_node)); lrm = first_named_child(cib_node, XML_CIB_TAG_LRM); if(lrm == NULL) { const char *node_uuid = ID(cib_node); lrm = create_xml_node(cib_node, XML_CIB_TAG_LRM); crm_xml_add(lrm, XML_ATTR_ID, node_uuid); } container = first_named_child(lrm, XML_LRM_TAG_RESOURCES); if(container == NULL) { container = create_xml_node(lrm, XML_LRM_TAG_RESOURCES); } cib_resource = create_xml_node(container, XML_LRM_TAG_RESOURCE); crm_xml_add(cib_resource, XML_ATTR_ID, resource); crm_xml_add(cib_resource, XML_AGENT_ATTR_CLASS, rclass); crm_xml_add(cib_resource, XML_AGENT_ATTR_PROVIDER, rprovider); crm_xml_add(cib_resource, XML_ATTR_TYPE, rtype); return cib_resource; } static lrm_op_t *create_op( xmlNode *cib_resource, const char *task, int interval, int outcome) { lrm_op_t *op = NULL; crm_malloc0(op, sizeof(lrm_op_t)); op->app_name = crm_strdup(crm_system_name); op->rsc_id = crm_strdup(ID(cib_resource)); op->interval = interval; op->op_type = crm_strdup(task); op->rc = outcome; op->op_status = 0; op->params = NULL; /* TODO: Fill me in */ op->call_id = 0; xml_child_iter(cib_resource, xop, int tmp = 0; crm_element_value_int(xop, XML_LRM_ATTR_CALLID, &tmp); if(tmp > op->call_id) { op->call_id = tmp; } ); op->call_id++; return op; } static xmlNode *inject_op(xmlNode *cib_resource, lrm_op_t *op, int target_rc) { return create_operation_update(cib_resource, op, CRM_FEATURE_SET, target_rc, crm_system_name); } static 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); crm_free(name); crm_free(now); } } static gboolean exec_pseudo_action(crm_graph_t *graph, crm_action_t *action) { action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static gboolean exec_rsc_action(crm_graph_t *graph, crm_action_t *action) { int rc = 0; lrm_op_t *op = NULL; int target_outcome = 0; const char *rtype = NULL; const char *rclass = NULL; const char *resource = NULL; const char *rprovider = NULL; const char *target_rc_s = crm_meta_value(action->params, XML_ATTR_TE_TARGET_RC); xmlNode *cib_op = NULL; xmlNode *cib_node = NULL; xmlNode *cib_object = NULL; xmlNode *cib_resource = NULL; xmlNode *action_rsc = first_named_child(action->xml, XML_CIB_TAG_RESOURCE); char *node = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); if(safe_str_eq(crm_element_value(action->xml, "operation"), "probe_complete")) { crm_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"); return FALSE; } resource = ID(action_rsc); rclass = crm_element_value(action_rsc, XML_AGENT_ATTR_CLASS); rtype = crm_element_value(action_rsc, XML_ATTR_TYPE); rprovider = crm_element_value(action_rsc, XML_AGENT_ATTR_PROVIDER); if(target_rc_s != NULL) { target_outcome = crm_parse_int(target_rc_s, "0"); } CRM_ASSERT(global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call|cib_scope_local) == cib_ok); cib_node = inject_node_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); quiet_log(" * Executing action %d: %s_%s_%d on %s\n", action->id, resource, op->op_type, op->interval, node); slist_iter(spec, char, op_fail, lpc, char *key = NULL; crm_malloc0(key, strlen(spec)); snprintf(key, strlen(spec), "%s_%s_%d@%s=", resource, op->op_type, op->interval, node); if(strncasecmp(key, spec, strlen(key)) == 0) { rc = sscanf(spec, "%*[^=]=%d", &op->rc); action->failed = TRUE; graph->abort_priority = INFINITY; printf("\tPretending action %d failed with rc=%d\n", action->id, op->rc); update_failcounts(cib_node, resource, op->interval, op->rc); break; } ); cib_op = inject_op(cib_resource, op, target_outcome); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); done: action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static gboolean exec_crmd_action(crm_graph_t *graph, crm_action_t *action) { action->confirmed = TRUE; update_graph(graph, action); return TRUE; } #define STATUS_PATH_MAX 512 static gboolean exec_stonith_action(crm_graph_t *graph, crm_action_t *action) { int rc = 0; char xpath[STATUS_PATH_MAX]; char *target = crm_element_value_copy(action->xml, XML_LRM_ATTR_TARGET); xmlNode *cib_node = modify_node(global_cib, target, FALSE); crm_xml_add(cib_node, XML_ATTR_ORIGIN, __FUNCTION__); CRM_ASSERT(cib_node != NULL); 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 == cib_ok); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_CIB_TAG_LRM); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); snprintf(xpath, STATUS_PATH_MAX, "//node_state[@uname='%s']/%s", target, XML_TAG_TRANSIENT_NODEATTRS); rc = global_cib->cmds->delete(global_cib, xpath, NULL, cib_xpath|cib_sync_call|cib_scope_local); action->confirmed = TRUE; update_graph(graph, action); return TRUE; } static char * add_list_element(char *list, const char *value) { int len = 0; int last = 0; if(value == NULL) { return list; } if(list) { last = strlen(list); } len = last + 2; /* +1 space, +1 EOS */ len += strlen(value); crm_realloc(list, len); sprintf(list + last, " %s", value); return list; } static void print_cluster_status(pe_working_set_t *data_set) { char *online_nodes = NULL; char *offline_nodes = NULL; slist_iter(node, node_t, data_set->nodes, lpc2, const char *node_mode = NULL; if(node->details->unclean) { if(node->details->online && node->details->unclean) { node_mode = "UNCLEAN (online)"; } else if(node->details->pending) { node_mode = "UNCLEAN (pending)"; } else { node_mode = "UNCLEAN (offline)"; } } else if(node->details->pending) { node_mode = "pending"; } else if(node->details->standby_onfail && node->details->online) { node_mode = "standby (on-fail)"; } else if(node->details->standby) { if(node->details->online) { node_mode = "standby"; } else { node_mode = "OFFLINE (standby)"; } } else if(node->details->online) { node_mode = "online"; online_nodes = add_list_element(online_nodes, node->details->uname); continue; } else { node_mode = "OFFLINE"; offline_nodes = add_list_element(offline_nodes, node->details->uname); continue; } if(safe_str_eq(node->details->uname, node->details->id)) { printf("Node %s: %s\n", node->details->uname, node_mode); } else { printf("Node %s (%s): %s\n", node->details->uname, node->details->id, node_mode); } ); if(online_nodes) { printf("Online: [%s ]\n", online_nodes); crm_free(online_nodes); } if(offline_nodes) { printf("OFFLINE: [%s ]\n", offline_nodes); crm_free(offline_nodes); } fprintf(stdout, "\n"); slist_iter(rsc, resource_t, data_set->resources, lpc, if(is_set(rsc->flags, pe_rsc_orphan) && rsc->role == RSC_ROLE_STOPPED) { continue; } rsc->fns->print(rsc, NULL, pe_print_printf, stdout); ); fprintf(stdout, "\n"); } static 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); do { graph_rc = run_graph(transition); } while(graph_rc == transition_active); if(graph_rc != transition_complete) { fprintf(stderr, "Transition failed: %s\n", transition_status(graph_rc)); print_graph(LOG_ERR, transition); } destroy_graph(transition); CRM_CHECK(graph_rc == transition_complete, fprintf(stderr, "An invalid transition was produced")); if(quiet == FALSE) { xmlNode *cib_object = NULL; ha_time_t *a_date = data_set->now; int rc = global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); quiet_log("\nRevised cluster status:\n"); set_working_set_defaults(data_set); data_set->input = cib_object; data_set->now = a_date; cluster_status(data_set); print_cluster_status(data_set); } if(graph_rc != transition_complete) { return graph_rc; } return 0; } static char * create_action_name(action_t *action) { char *action_name = NULL; const char *action_host = NULL; if(action->node) { action_host = action->node->details->uname; action_name = crm_concat(action->uuid, action_host, ' '); } else if(action->pseudo) { action_name = crm_strdup(action->uuid); } else { action_host = ""; action_name = crm_concat(action->uuid, action_host, ' '); } if(safe_str_eq(action->task, RSC_CANCEL)) { char *tmp_action_name = action_name; action_name = crm_concat("Cancel", tmp_action_name, ' '); crm_free(tmp_action_name); } return action_name; } static void create_dotfile(pe_working_set_t *data_set, const char *dot_file, gboolean all_actions) { FILE *dot_strm = fopen(dot_file, "w"); if(dot_strm == NULL) { crm_perror(LOG_ERR,"Could not open %s for writing", dot_file); return; } fprintf(dot_strm, " digraph \"g\" {\n"); slist_iter( action, action_t, data_set->actions, lpc, const char *style = "filled"; const char *font = "black"; const char *color = "black"; const char *fill = NULL; char *action_name = create_action_name(action); crm_debug_3("Action %d: %p", action->id, action); if(action->pseudo) { font = "orange"; } style = "dashed"; if(action->dumped) { style = "bold"; color = "green"; } else if(action->rsc != NULL && is_not_set(action->rsc->flags, pe_rsc_managed)) { color = "purple"; if(all_actions == FALSE) { goto dont_write; } } else if(action->optional) { color = "blue"; if(all_actions == FALSE) { goto dont_write; } } else { color = "red"; CRM_CHECK(action->runnable == FALSE, ;); } action->dumped = TRUE; fprintf(dot_strm, "\"%s\" [ style=%s color=\"%s\" fontcolor=\"%s\" %s%s]\n", action_name, style, color, font, fill?"fillcolor=":"", fill?fill:""); dont_write: crm_free(action_name); ); slist_iter( action, action_t, data_set->actions, lpc, slist_iter( before, action_wrapper_t, action->actions_before, lpc2, char *before_name = NULL; char *after_name = NULL; const char *style = "dashed"; gboolean optional = TRUE; if(before->state == pe_link_dumped) { optional = FALSE; style = "bold"; } else if(action->pseudo && (before->type & pe_order_stonith_stop)) { continue; } else if(before->state == pe_link_dup) { continue; } else if(before->type == pe_order_none) { continue; } else if(action->dumped && before->action->dumped) { optional = FALSE; } if(all_actions || optional == FALSE) { before_name = create_action_name(before->action); after_name = create_action_name(action); fprintf(dot_strm, "\"%s\" -> \"%s\" [ style = %s]\n", before_name, after_name, style); crm_free(before_name); crm_free(after_name); } ); ); fprintf(dot_strm, "}\n"); if(dot_strm != NULL) { fflush(dot_strm); fclose(dot_strm); } } static void modify_configuration( pe_working_set_t *data_set, const char *quorum, GListPtr node_up, GListPtr node_down, GListPtr node_fail, GListPtr op_inject) { int rc = cib_ok; xmlNode *cib_op = NULL; xmlNode *cib_node = NULL; xmlNode *cib_resource = NULL; lrm_op_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 == cib_ok); } slist_iter(node, char, node_up, lpc, 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 == cib_ok); ); slist_iter(node, char, node_down, lpc, 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 == cib_ok); ); slist_iter(node, char, node_fail, lpc, quiet_log(" + Failing node %s\n", node); cib_node = modify_node(global_cib, node, TRUE); crm_xml_add(cib_node, XML_CIB_ATTR_INCCM, XML_BOOLEAN_NO); CRM_ASSERT(cib_node != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); ); slist_iter(spec, char, op_inject, lpc, int rc = 0; int outcome = 0; int interval = 0; char *key = NULL; char *node = NULL; char *task = NULL; char *resource = NULL; const char *rtype = NULL; const char *rclass = NULL; const char *rprovider = NULL; resource_t *rsc = NULL; quiet_log(" + Injecting %s into the configuration\n", spec); crm_malloc0(key, strlen(spec)); crm_malloc0(node, strlen(spec)); rc = sscanf(spec, "%[^@]@%[^=]=%d", key, node, &outcome); CRM_CHECK(rc == 3, fprintf(stderr, "Invalid operation spec: %s. Only found %d fields\n", spec, rc); continue); parse_op_key(key, &resource, &task, &interval); rsc = pe_find_resource(data_set->resources, resource); CRM_CHECK(rsc != NULL, fprintf(stderr, "Invalid resource name: %s\n", resource); continue); rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS); rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE); rprovider = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER); cib_node = inject_node_state(global_cib, node); CRM_ASSERT(cib_node != NULL); update_failcounts(cib_node, resource, interval, rc); cib_resource = inject_resource(cib_node, resource, rclass, rtype, rprovider); CRM_ASSERT(cib_resource != NULL); op = create_op(cib_resource, task, interval, outcome); CRM_ASSERT(op != NULL); cib_op = inject_op(cib_resource, op, 0); CRM_ASSERT(cib_op != NULL); rc = global_cib->cmds->modify(global_cib, XML_CIB_TAG_STATUS, cib_node, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); ); } static void setup_input(const char *input, const char *output) { int rc = cib_ok; cib_t *cib_conn = NULL; xmlNode *cib_object = 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 == cib_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"); exit(3); } } else if(safe_str_eq(input, "-")) { cib_object = filename2xml(NULL); } else { cib_object = filename2xml(input); } if(cli_config_update(&cib_object, NULL, FALSE) == FALSE) { free_xml(cib_object); exit(cib_STALE); } if(validate_xml(cib_object, NULL, FALSE) != TRUE) { free_xml(cib_object); exit(cib_dtd_validation); } if(output == NULL) { char *pid = crm_itoa(getpid()); output = get_shadow_file(pid); crm_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)); exit(rc); } setenv("CIB_file", output, 1); } 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"}, {"-spacer-", 0, 0, '-', "\nSynthetic Cluster Events:"}, {"node-up", 1, 0, 'u', "\tBring a node online"}, {"node-down", 1, 0, 'd', "\tTake a node offline"}, {"node-fail", 1, 0, 'f', "\tMark a node as failed"}, {"op-inject", 1, 0, 'i', "\t$rsc_$task_$interval@$node=$rc - Inject the specified task before running the simulation"}, {"op-fail", 1, 0, 'F', "\t$rsc_$task_$interval@$node=$rc - Fail the specified task while running the simulation"}, {"set-datetime", 1, 0, 't', "Set date/time"}, {"quorum", 1, 0, 'q', "\tSpecify a value for quorum"}, {"-spacer-", 0, 0, '-', "\nOutput Options:"}, {"save-input", 1, 0, 'I', "\tSave the input configuration to the named file"}, {"save-output", 1, 0, 'O', "Save the output configuration to the named file"}, {"save-graph", 1, 0, 'G', "\tSave the transition graph (XML format) to the named file"}, {"save-dotfile", 1, 0, 'D', "Save the transition graph (DOT format) to the named file"}, {"all-actions", 0, 0, 'a', "\tDisplay all possible actions in the DOT graph - even ones not part of the transition"}, {"-spacer-", 0, 0, '-', "\nData Source:"}, {"live-check", 0, 0, 'L', "\tConnect to the CIB and use the current contents as input"}, {"xml-file", 1, 0, 'x', "\tRetrieve XML from the named file"}, {"xml-pipe", 0, 0, 'p', "\tRetrieve XML from stdin"}, {0, 0, 0, 0} }; int main(int argc, char ** argv) { int rc = 0; guint modified = 0; gboolean store = FALSE; gboolean process = FALSE; gboolean verbose = FALSE; gboolean simulate = FALSE; gboolean all_actions = FALSE; pe_working_set_t data_set; ha_time_t *a_date = NULL; const char *xml_file = "-"; const char *quorum = NULL; const char *dot_file = NULL; const char *graph_file = NULL; const char *input_file = NULL; const char *output_file = NULL; int flag = 0; int index = 0; int argerr = 0; char *use_date = NULL; GListPtr node_up = NULL; GListPtr node_down = NULL; GListPtr node_fail = NULL; GListPtr op_inject = NULL; xmlNode *input = NULL; crm_log_init("crm_simulate", LOG_ERR, FALSE, FALSE, argc, argv); - crm_set_options("?$VQx:Lpu:d:f:i:RSXD:G:I:O:saF:t:", "datasource operation [additional options]", + crm_set_options("?$VQx:Lpu:d:f:i:RSXD:G:I:O:sUaF:t:", "datasource operation [additional options]", long_options, "Tool for simulating the cluster's response to events"); if(argc < 2) { crm_help('?', LSB_EXIT_EINVAL); } while (1) { flag = crm_get_option(argc, argv, &index); if (flag == -1) break; switch(flag) { case 'V': verbose = TRUE; alter_debug(DEBUG_INC); cl_log_enable_stderr(TRUE); break; case '?': case '$': crm_help(flag, LSB_EXIT_OK); break; case 'p': xml_file = "-"; break; case 'Q': quiet = TRUE; break; case 'L': xml_file = NULL; break; case 'x': xml_file = optarg; break; case 'u': 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 = crm_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 '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': simulate = TRUE; output_file = optarg; break; default: ++argerr; break; } } if (optind > argc) { ++argerr; } if (argerr) { crm_help('?', LSB_EXIT_GENERIC); } setup_input(xml_file, store?xml_file:output_file); global_cib = cib_new(); global_cib->cmds->signon(global_cib, crm_system_name, cib_command); if(use_date != NULL) { a_date = parse_date(&use_date); quiet_log(" + Setting effective cluster time: %s", use_date); log_date(LOG_WARNING, "Set fake 'now' to", a_date, ha_log_date|ha_log_time); } if(quiet == FALSE) { xmlNode *cib_object = NULL; rc = global_cib->cmds->query(global_cib, NULL, &cib_object, cib_sync_call|cib_scope_local); CRM_ASSERT(rc == cib_ok); set_working_set_defaults(&data_set); data_set.input = cib_object; data_set.now = a_date; cluster_status(&data_set); quiet_log("\nCurrent cluster status:\n"); print_cluster_status(&data_set); if(process == FALSE && modified == FALSE) { return 0; } } if(modified) { quiet_log("Performing requested modifications\n"); modify_configuration(&data_set, quorum, node_up, node_down, node_fail, op_inject); } rc = global_cib->cmds->query(global_cib, NULL, &input, cib_sync_call); if(rc != cib_ok) { fprintf(stderr, "Could not connect to the CIB for input: %s\n", cib_error2string(rc)); return rc; } 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)); return rc; } free_xml(input); } rc = 0; if(process || simulate) { - if(show_scores) { + if(show_scores && show_utilization) { + printf("Allocation scores and utilization information:\n"); + } else if(show_scores) { printf("Allocation scores:\n"); + } else if(show_utilization) { + printf("Utilization information:\n"); } do_calculations(&data_set, input, a_date); if(graph_file != NULL) { char *msg_buffer = dump_xml_formatted(data_set.graph); FILE *graph_strm = fopen(graph_file, "w"); if(graph_strm == NULL) { crm_perror(LOG_ERR,"Could not open %s for writing", graph_file); } else { if(fprintf(graph_strm, "%s\n\n", msg_buffer) < 0) { crm_perror(LOG_ERR,"Write to %s failed", graph_file); } fflush(graph_strm); fclose(graph_strm); } crm_free(msg_buffer); } if(dot_file != NULL) { create_dotfile(&data_set, dot_file, all_actions); } if(quiet == FALSE && verbose == FALSE) { - quiet_log("%sTransition Summary:\n", show_scores||modified?"\n":""); + quiet_log("%sTransition Summary:\n", show_scores||show_utilization||modified?"\n":""); fflush(stdout); crm_log_level = LOG_NOTICE; cl_log_enable_stderr(TRUE); slist_iter( rsc, resource_t, data_set.resources, lpc, LogActions(rsc, &data_set); ); cl_log_enable_stderr(FALSE); } } if(simulate) { rc = run_simulation(&data_set); } global_cib->cmds->signoff(global_cib); cib_delete(global_cib); fflush(stderr); return rc; }