| /* |
| * Copyright (C) 2014 Gao,Yan <ygao@suse.com> |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This software is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| */ |
| |
| #include <crm_internal.h> |
| #include <crm/msg_xml.h> |
| #include <allocate.h> |
| #include <utils.h> |
| |
| static GListPtr find_colocated_rscs(GListPtr colocated_rscs, resource_t * rsc, |
| resource_t * orig_rsc); |
| |
| static GListPtr group_find_colocated_rscs(GListPtr colocated_rscs, resource_t * rsc, |
| resource_t * orig_rsc); |
| |
| static void group_add_unallocated_utilization(GHashTable * all_utilization, resource_t * rsc, |
| GListPtr all_rscs); |
| |
| 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 |
| */ |
| 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; |
| } |
| |
| struct calculate_data { |
| GHashTable *current_utilization; |
| gboolean plus; |
| }; |
| |
| static void |
| do_calculate_utilization(gpointer key, gpointer value, gpointer user_data) |
| { |
| const char *current = NULL; |
| char *result = NULL; |
| struct calculate_data *data = user_data; |
| |
| current = g_hash_table_lookup(data->current_utilization, key); |
| if (data->plus) { |
| result = crm_itoa(crm_parse_int(current, "0") + crm_parse_int(value, "0")); |
| g_hash_table_replace(data->current_utilization, strdup(key), result); |
| |
| } else if (current) { |
| result = crm_itoa(crm_parse_int(current, "0") - crm_parse_int(value, "0")); |
| g_hash_table_replace(data->current_utilization, strdup(key), result); |
| } |
| } |
| |
| /* Specify 'plus' to FALSE when allocating |
| * Otherwise to TRUE when deallocating |
| */ |
| void |
| calculate_utilization(GHashTable * current_utilization, |
| GHashTable * utilization, gboolean plus) |
| { |
| struct calculate_data data; |
| |
| data.current_utilization = current_utilization; |
| data.plus = plus; |
| |
| g_hash_table_foreach(utilization, do_calculate_utilization, &data); |
| } |
| |
| |
| struct capacity_data { |
| node_t *node; |
| const char *rsc_id; |
| gboolean is_enough; |
| }; |
| |
| static void |
| check_capacity(gpointer key, gpointer value, gpointer user_data) |
| { |
| int required = 0; |
| int remaining = 0; |
| struct capacity_data *data = user_data; |
| |
| required = crm_parse_int(value, "0"); |
| remaining = crm_parse_int(g_hash_table_lookup(data->node->details->utilization, key), "0"); |
| |
| if (required > remaining) { |
| CRM_ASSERT(data->rsc_id); |
| CRM_ASSERT(data->node); |
| |
| crm_debug("Node %s does not have enough %s for %s: required=%d remaining=%d", |
| data->node->details->uname, (char *)key, data->rsc_id, required, remaining); |
| data->is_enough = FALSE; |
| } |
| } |
| |
| static gboolean |
| have_enough_capacity(node_t * node, const char * rsc_id, GHashTable * utilization) |
| { |
| struct capacity_data data; |
| |
| data.node = node; |
| data.rsc_id = rsc_id; |
| data.is_enough = TRUE; |
| |
| g_hash_table_foreach(utilization, check_capacity, &data); |
| |
| return data.is_enough; |
| } |
| |
| |
| static void |
| native_add_unallocated_utilization(GHashTable * all_utilization, resource_t * rsc) |
| { |
| if(is_set(rsc->flags, pe_rsc_provisional) == FALSE) { |
| return; |
| } |
| |
| calculate_utilization(all_utilization, rsc->utilization, TRUE); |
| } |
| |
| static void |
| add_unallocated_utilization(GHashTable * all_utilization, resource_t * rsc, |
| GListPtr all_rscs, resource_t * orig_rsc) |
| { |
| if(is_set(rsc->flags, pe_rsc_provisional) == FALSE) { |
| return; |
| } |
| |
| if (rsc->variant == pe_native) { |
| pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization", |
| orig_rsc->id, rsc->id); |
| native_add_unallocated_utilization(all_utilization, rsc); |
| |
| } else if (rsc->variant == pe_group) { |
| pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization", |
| orig_rsc->id, rsc->id); |
| group_add_unallocated_utilization(all_utilization, rsc, all_rscs); |
| |
| } else if (pe_rsc_is_clone(rsc)) { |
| GListPtr gIter1 = NULL; |
| gboolean existing = FALSE; |
| |
| /* Check if there's any child already existing in the list */ |
| gIter1 = rsc->children; |
| for (; gIter1 != NULL; gIter1 = gIter1->next) { |
| resource_t *child = (resource_t *) gIter1->data; |
| GListPtr gIter2 = NULL; |
| |
| if (g_list_find(all_rscs, child)) { |
| existing = TRUE; |
| |
| } else { |
| /* Check if there's any child of another cloned group already existing in the list */ |
| gIter2 = child->children; |
| for (; gIter2 != NULL; gIter2 = gIter2->next) { |
| resource_t *grandchild = (resource_t *) gIter2->data; |
| |
| if (g_list_find(all_rscs, grandchild)) { |
| pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization", |
| orig_rsc->id, child->id); |
| add_unallocated_utilization(all_utilization, child, all_rscs, orig_rsc); |
| existing = TRUE; |
| break; |
| } |
| } |
| } |
| } |
| |
| // rsc->children is always non-NULL but this makes static analysis happy |
| if (!existing && (rsc->children != NULL)) { |
| resource_t *first_child = (resource_t *) rsc->children->data; |
| |
| pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization", |
| orig_rsc->id, ID(first_child->xml)); |
| add_unallocated_utilization(all_utilization, first_child, all_rscs, orig_rsc); |
| } |
| } |
| } |
| |
| static GHashTable * |
| sum_unallocated_utilization(resource_t * rsc, GListPtr colocated_rscs) |
| { |
| GListPtr gIter = NULL; |
| GListPtr all_rscs = NULL; |
| GHashTable *all_utilization = crm_str_table_new(); |
| |
| all_rscs = g_list_copy(colocated_rscs); |
| if (g_list_find(all_rscs, rsc) == FALSE) { |
| all_rscs = g_list_append(all_rscs, rsc); |
| } |
| |
| for (gIter = all_rscs; gIter != NULL; gIter = gIter->next) { |
| resource_t *listed_rsc = (resource_t *) gIter->data; |
| |
| if(is_set(listed_rsc->flags, pe_rsc_provisional) == FALSE) { |
| continue; |
| } |
| |
| pe_rsc_trace(rsc, "%s: Processing unallocated colocated %s", rsc->id, listed_rsc->id); |
| add_unallocated_utilization(all_utilization, listed_rsc, all_rscs, rsc); |
| } |
| |
| g_list_free(all_rscs); |
| |
| return all_utilization; |
| } |
| |
| static GListPtr |
| find_colocated_rscs(GListPtr colocated_rscs, resource_t * rsc, resource_t * orig_rsc) |
| { |
| GListPtr gIter = NULL; |
| |
| if (rsc == NULL) { |
| return colocated_rscs; |
| |
| } else if (g_list_find(colocated_rscs, rsc)) { |
| return colocated_rscs; |
| } |
| |
| crm_trace("%s: %s is supposed to be colocated with %s", orig_rsc->id, rsc->id, orig_rsc->id); |
| colocated_rscs = g_list_append(colocated_rscs, rsc); |
| |
| for (gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) { |
| rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; |
| resource_t *rsc_rh = constraint->rsc_rh; |
| |
| /* Break colocation loop */ |
| if (rsc_rh == orig_rsc) { |
| continue; |
| } |
| |
| if (constraint->score == INFINITY |
| && filter_colocation_constraint(rsc, rsc_rh, constraint, TRUE) == influence_rsc_location) { |
| |
| if (rsc_rh->variant == pe_group) { |
| /* Need to use group_variant_data */ |
| colocated_rscs = group_find_colocated_rscs(colocated_rscs, rsc_rh, orig_rsc); |
| |
| } else { |
| colocated_rscs = find_colocated_rscs(colocated_rscs, rsc_rh, orig_rsc); |
| } |
| } |
| } |
| |
| for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) { |
| rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data; |
| resource_t *rsc_lh = constraint->rsc_lh; |
| |
| /* Break colocation loop */ |
| if (rsc_lh == orig_rsc) { |
| continue; |
| } |
| |
| if (pe_rsc_is_clone(rsc_lh) == FALSE && pe_rsc_is_clone(rsc)) { |
| /* We do not know if rsc_lh will be colocated with orig_rsc in this case */ |
| continue; |
| } |
| |
| if (constraint->score == INFINITY |
| && filter_colocation_constraint(rsc_lh, rsc, constraint, TRUE) == influence_rsc_location) { |
| |
| if (rsc_lh->variant == pe_group) { |
| /* Need to use group_variant_data */ |
| colocated_rscs = group_find_colocated_rscs(colocated_rscs, rsc_lh, orig_rsc); |
| |
| } else { |
| colocated_rscs = find_colocated_rscs(colocated_rscs, rsc_lh, orig_rsc); |
| } |
| } |
| } |
| |
| return colocated_rscs; |
| } |
| |
| void |
| process_utilization(resource_t * rsc, node_t ** prefer, pe_working_set_t * data_set) |
| { |
| CRM_CHECK(rsc && prefer && data_set, return); |
| if (safe_str_neq(data_set->placement_strategy, "default")) { |
| GHashTableIter iter; |
| GListPtr colocated_rscs = NULL; |
| gboolean any_capable = FALSE; |
| node_t *node = NULL; |
| |
| colocated_rscs = find_colocated_rscs(colocated_rscs, rsc, rsc); |
| if (colocated_rscs) { |
| GHashTable *unallocated_utilization = NULL; |
| char *rscs_id = crm_concat(rsc->id, "and its colocated resources", ' '); |
| node_t *most_capable_node = NULL; |
| |
| unallocated_utilization = sum_unallocated_utilization(rsc, colocated_rscs); |
| |
| g_hash_table_iter_init(&iter, rsc->allowed_nodes); |
| while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { |
| if (can_run_resources(node) == FALSE || node->weight < 0) { |
| continue; |
| } |
| |
| if (have_enough_capacity(node, rscs_id, unallocated_utilization)) { |
| any_capable = TRUE; |
| } |
| |
| if (most_capable_node == NULL || |
| compare_capacity(node, most_capable_node) < 0) { |
| /* < 0 means 'node' is more capable */ |
| most_capable_node = node; |
| } |
| } |
| |
| if (any_capable) { |
| g_hash_table_iter_init(&iter, rsc->allowed_nodes); |
| while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { |
| if (can_run_resources(node) == FALSE || node->weight < 0) { |
| continue; |
| } |
| |
| if (have_enough_capacity(node, rscs_id, unallocated_utilization) == FALSE) { |
| pe_rsc_debug(rsc, |
| "Resource %s and its colocated resources" |
| " cannot be allocated to node %s: not enough capacity", |
| rsc->id, node->details->uname); |
| resource_location(rsc, node, -INFINITY, "__limit_utilization__", data_set); |
| } |
| } |
| |
| } else if (*prefer == NULL) { |
| *prefer = most_capable_node; |
| } |
| |
| if (unallocated_utilization) { |
| g_hash_table_destroy(unallocated_utilization); |
| } |
| |
| g_list_free(colocated_rscs); |
| free(rscs_id); |
| } |
| |
| if (any_capable == FALSE) { |
| g_hash_table_iter_init(&iter, rsc->allowed_nodes); |
| while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) { |
| if (can_run_resources(node) == FALSE || node->weight < 0) { |
| continue; |
| } |
| |
| if (have_enough_capacity(node, rsc->id, rsc->utilization) == FALSE) { |
| pe_rsc_debug(rsc, |
| "Resource %s cannot be allocated to node %s:" |
| " not enough capacity", |
| rsc->id, node->details->uname); |
| resource_location(rsc, node, -INFINITY, "__limit_utilization__", data_set); |
| } |
| } |
| } |
| pe__show_node_weights(true, rsc, "Post-utilization", rsc->allowed_nodes); |
| } |
| } |
| |
| #define VARIANT_GROUP 1 |
| #include <lib/pengine/variant.h> |
| |
| GListPtr |
| group_find_colocated_rscs(GListPtr colocated_rscs, resource_t * rsc, resource_t * orig_rsc) |
| { |
| group_variant_data_t *group_data = NULL; |
| |
| get_group_variant_data(group_data, rsc); |
| if (group_data->colocated || pe_rsc_is_clone(rsc->parent)) { |
| GListPtr gIter = rsc->children; |
| |
| for (; gIter != NULL; gIter = gIter->next) { |
| resource_t *child_rsc = (resource_t *) gIter->data; |
| |
| colocated_rscs = find_colocated_rscs(colocated_rscs, child_rsc, orig_rsc); |
| } |
| |
| } else { |
| if (group_data->first_child) { |
| colocated_rscs = find_colocated_rscs(colocated_rscs, group_data->first_child, orig_rsc); |
| } |
| } |
| |
| colocated_rscs = find_colocated_rscs(colocated_rscs, rsc, orig_rsc); |
| |
| return colocated_rscs; |
| } |
| |
| static void |
| group_add_unallocated_utilization(GHashTable * all_utilization, resource_t * rsc, |
| GListPtr all_rscs) |
| { |
| group_variant_data_t *group_data = NULL; |
| |
| get_group_variant_data(group_data, rsc); |
| if (group_data->colocated || pe_rsc_is_clone(rsc->parent)) { |
| GListPtr gIter = rsc->children; |
| |
| for (; gIter != NULL; gIter = gIter->next) { |
| resource_t *child_rsc = (resource_t *) gIter->data; |
| |
| if (is_set(child_rsc->flags, pe_rsc_provisional) && |
| g_list_find(all_rscs, child_rsc) == FALSE) { |
| native_add_unallocated_utilization(all_utilization, child_rsc); |
| } |
| } |
| |
| } else { |
| if (group_data->first_child && |
| is_set(group_data->first_child->flags, pe_rsc_provisional) && |
| g_list_find(all_rscs, group_data->first_child) == FALSE) { |
| native_add_unallocated_utilization(all_utilization, group_data->first_child); |
| } |
| } |
| } |
| |
| |