commit 0d14f1ed8915cba2f109068ca8b968e7ed139de4
Author: Gao,Yan <ygao@suse.com>
Date:   Tue Jul 8 12:06:59 2014 +0800

    Fix: pengine: cl#5130 - Choose nodes capable of running all the colocated utilization resources
    
    If no node is capable of hosting the entire set of them, the most
    capable node will be chosen.

diff --git a/pengine/Makefile.am b/pengine/Makefile.am
index e67da7b..acbe542 100644
--- a/pengine/Makefile.am
+++ b/pengine/Makefile.am
@@ -64,7 +64,7 @@ noinst_HEADERS	= allocate.h utils.h pengine.h
 libpengine_la_LDFLAGS	= -version-info 8:0:0
 # -L$(top_builddir)/lib/pils -lpils -export-dynamic -module -avoid-version
 libpengine_la_SOURCES	= pengine.c allocate.c utils.c constraints.c
-libpengine_la_SOURCES  += native.c group.c clone.c master.c graph.c
+libpengine_la_SOURCES  += native.c group.c clone.c master.c graph.c utilization.c
 
 libpengine_la_LIBADD    = $(top_builddir)/lib/pengine/libpe_status.la \
 	$(top_builddir)/lib/cib/libcib.la
diff --git a/pengine/native.c b/pengine/native.c
index 0d5842f..6df7c48 100644
--- a/pengine/native.c
+++ b/pengine/native.c
@@ -79,49 +79,8 @@ gboolean (*rsc_action_matrix[RSC_ROLE_MAX][RSC_ROLE_MAX])(resource_t*,node_t*,gb
 };
 /* *INDENT-ON* */
 
-struct capacity_data {
-    node_t *node;
-    resource_t *rsc;
-    gboolean is_enough;
-};
-
 static action_t * get_first_named_action(resource_t * rsc, const char *action, gboolean only_valid, node_t * current);
 
-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);
-        CRM_ASSERT(data->node);
-
-        pe_rsc_debug(data->rsc,
-                     "Node %s has no enough %s for resource %s: required=%d remaining=%d",
-                     data->node->details->uname, (char *)key, data->rsc->id, required, remaining);
-        data->is_enough = FALSE;
-    }
-}
-
-static gboolean
-have_enough_capacity(node_t * node, resource_t * rsc)
-{
-    struct capacity_data data;
-
-    data.node = node;
-    data.rsc = rsc;
-    data.is_enough = TRUE;
-
-    g_hash_table_foreach(rsc->utilization, check_capacity, &data);
-
-    return data.is_enough;
-}
-
 static gboolean
 native_choose_node(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set)
 {
@@ -131,8 +90,6 @@ native_choose_node(resource_t * rsc, node_t * prefer, pe_working_set_t * data_se
        with the fewest resources
        3. remove color.chosen_node from all other colors
      */
-    int alloc_details = scores_log_level + 1;
-
     GListPtr nodes = NULL;
     node_t *chosen = NULL;
 
@@ -141,21 +98,7 @@ native_choose_node(resource_t * rsc, node_t * prefer, pe_working_set_t * data_se
     int length = 0;
     gboolean result = FALSE;
 
-    if (safe_str_neq(data_set->placement_strategy, "default")) {
-        GListPtr gIter = NULL;
-
-        for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
-            node_t *node = (node_t *) gIter->data;
-
-            if (have_enough_capacity(node, rsc) == FALSE) {
-                pe_rsc_debug(rsc,
-                             "Resource %s cannot be allocated to node %s: none of enough capacity",
-                             rsc->id, node->details->uname);
-                resource_location(rsc, node, -INFINITY, "__limit_utilization_", data_set);
-            }
-        }
-        dump_node_scores(alloc_details, rsc, "Post-utilization", rsc->allowed_nodes);
-    }
+    process_utilization(rsc, &prefer, data_set);
 
     length = g_hash_table_size(rsc->allowed_nodes);
 
@@ -1536,22 +1479,16 @@ native_rsc_colocation_lh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocatio
     rsc_rh->cmds->rsc_colocation_rh(rsc_lh, rsc_rh, constraint);
 }
 
-enum filter_colocation_res {
-    influence_nothing = 0,
-    influence_rsc_location,
-    influence_rsc_priority,
-};
-
-static enum filter_colocation_res
+enum filter_colocation_res
 filter_colocation_constraint(resource_t * rsc_lh, resource_t * rsc_rh,
-                             rsc_colocation_t * constraint)
+                             rsc_colocation_t * constraint, gboolean preview)
 {
     if (constraint->score == 0) {
         return influence_nothing;
     }
 
     /* rh side must be allocated before we can process constraint */
-    if (is_set(rsc_rh->flags, pe_rsc_provisional)) {
+    if (preview == FALSE && is_set(rsc_rh->flags, pe_rsc_provisional)) {
         return influence_nothing;
     }
 
@@ -1564,7 +1501,7 @@ filter_colocation_constraint(resource_t * rsc_lh, resource_t * rsc_rh,
         return influence_rsc_priority;
     }
 
-    if (is_not_set(rsc_lh->flags, pe_rsc_provisional)) {
+    if (preview == FALSE && is_not_set(rsc_lh->flags, pe_rsc_provisional)) {
         /* error check */
         struct node_shared_s *details_lh;
         struct node_shared_s *details_rh;
@@ -1733,7 +1670,7 @@ native_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocatio
 
     CRM_ASSERT(rsc_lh);
     CRM_ASSERT(rsc_rh);
-    filter_results = filter_colocation_constraint(rsc_lh, rsc_rh, constraint);
+    filter_results = filter_colocation_constraint(rsc_lh, rsc_rh, constraint, FALSE);
     pe_rsc_trace(rsc_lh, "%sColocating %s with %s (%s, weight=%d, filter=%d)",
                  constraint->score >= 0 ? "" : "Anti-",
                  rsc_lh->id, rsc_rh->id, constraint->id, constraint->score, filter_results);
diff --git a/pengine/utilization.c b/pengine/utilization.c
new file mode 100644
index 0000000..982fcc9
--- /dev/null
+++ b/pengine/utilization.c
@@ -0,0 +1,472 @@
+/*
+ * 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 has no 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 (rsc->variant == pe_clone ||
+               rsc->variant == pe_master) {
+        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;
+                    }
+                }
+            }
+        }
+
+        if (existing == FALSE) {
+            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 = g_hash_table_new_full(crm_str_hash, g_str_equal,
+                                          g_hash_destroy_str, g_hash_destroy_str);
+
+    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 (rsc_lh->variant <= pe_group && rsc->variant > pe_group) {
+            /* 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)
+{
+    int alloc_details = scores_log_level + 1;
+
+    if (safe_str_neq(data_set->placement_strategy, "default")) {
+        GListPtr gIter = NULL;
+        GListPtr colocated_rscs = NULL;
+        gboolean any_capable = FALSE;
+
+        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);
+
+            for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
+                node_t *node = (node_t *) gIter->data;
+
+                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) {
+                for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
+                    node_t *node = (node_t *) gIter->data;
+
+                    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: no 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) {
+            for (gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
+                node_t *node = (node_t *) gIter->data;
+
+                if (have_enough_capacity(node, rsc->id, rsc->utilization) == FALSE) {
+                    pe_rsc_debug(rsc, "Resource %s cannot be allocated to node %s: no enough capacity",
+                                 rsc->id, node->details->uname);
+                    resource_location(rsc, node, -INFINITY, "__limit_utilization__", data_set);
+                }
+            }
+        }
+        dump_node_scores(alloc_details, rsc, "Post-utilization", rsc->allowed_nodes);
+    }
+}
+
+#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 ||
+        (rsc->parent &&
+         (rsc->parent->variant == pe_clone || rsc->parent->variant == pe_master))) {
+        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 ||
+        (rsc->parent &&
+         (rsc->parent->variant == pe_clone || rsc->parent->variant == pe_master))) {
+        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);
+        }
+    }
+}
+
+
diff --git a/pengine/utils.c b/pengine/utils.c
index 7dfaf95..001853b 100644
--- a/pengine/utils.c
+++ b/pengine/utils.c
@@ -116,69 +116,6 @@ can_run_resources(const node_t * node)
     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
  */
@@ -268,47 +205,6 @@ sort_node_weight(gconstpointer a, gconstpointer b, gpointer data)
     return strcmp(node1->details->uname, node2->details->uname);
 }
 
-struct calculate_data {
-    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, 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);
-    }
-}
-
 void
 native_deallocate(resource_t * rsc)
 {
@@ -322,7 +218,7 @@ native_deallocate(resource_t * rsc)
         old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc, rsc);
         old->details->num_resources--;
         /* old->count--; */
-        calculate_utilization(old, rsc, FALSE);
+        calculate_utilization(old->details->utilization, rsc->utilization, TRUE);
         free(old);
     }
 }
@@ -389,7 +285,9 @@ native_assign_node(resource_t * rsc, GListPtr nodes, node_t * chosen, gboolean f
     chosen->details->allocated_rsc = g_list_prepend(chosen->details->allocated_rsc, rsc);
     chosen->details->num_resources++;
     chosen->count++;
-    calculate_utilization(chosen, rsc, TRUE);
+    calculate_utilization(chosen->details->utilization, rsc->utilization, FALSE);
+    dump_rsc_utilization(show_utilization ? 0 : utilization_log_level, __FUNCTION__, rsc, chosen);
+
     return TRUE;
 }
 
diff --git a/pengine/utils.h b/pengine/utils.h
index 1939d40..e331348 100644
--- a/pengine/utils.h
+++ b/pengine/utils.h
@@ -55,6 +55,23 @@ extern gboolean can_run_any(GHashTable * nodes);
 extern resource_t *find_compatible_child(resource_t * local_child, resource_t * rsc,
                                          enum rsc_role_e filter, gboolean current);
 
+
+enum filter_colocation_res {
+    influence_nothing = 0,
+    influence_rsc_location,
+    influence_rsc_priority,
+};
+
+extern enum filter_colocation_res
+filter_colocation_constraint(resource_t * rsc_lh, resource_t * rsc_rh,
+                             rsc_colocation_t * constraint, gboolean preview);
+
+extern int compare_capacity(const node_t * node1, const node_t * node2);
+extern void calculate_utilization(GHashTable * current_utilization,
+                                  GHashTable * utilization, gboolean plus);
+
+extern void process_utilization(resource_t * rsc, node_t ** prefer, pe_working_set_t * data_set);
+
 #  define STONITH_UP "stonith_up"
 #  define STONITH_DONE "stonith_complete"
 #  define ALL_STOPPED "all_stopped"
