Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4624395
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
80 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/pengine/clone.c b/pengine/clone.c
index 2b332b16df..f8c750361f 100644
--- a/pengine/clone.c
+++ b/pengine/clone.c
@@ -1,1658 +1,1667 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <notif.h>
#include <utils.h>
#include <allocate.h>
#define VARIANT_CLONE 1
#include <lib/pengine/variant.h>
gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
static void append_parent_colocation(resource_t * rsc, resource_t * child, gboolean all);
static gint
sort_rsc_id(gconstpointer a, gconstpointer b)
{
const resource_t *resource1 = (const resource_t *)a;
const resource_t *resource2 = (const resource_t *)b;
CRM_ASSERT(resource1 != NULL);
CRM_ASSERT(resource2 != NULL);
return strcmp(resource1->id, resource2->id);
}
static node_t *
parent_node_instance(const resource_t * rsc, node_t * node)
{
node_t *ret = NULL;
if (node != NULL && rsc->parent) {
ret = pe_hash_table_lookup(rsc->parent->allowed_nodes, node->details->id);
} else if(node != NULL) {
ret = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
}
return ret;
}
static gboolean
did_fail(const resource_t * rsc)
{
GListPtr gIter = rsc->children;
if (is_set(rsc->flags, pe_rsc_failed)) {
return TRUE;
}
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
if (did_fail(child_rsc)) {
return TRUE;
}
}
return FALSE;
}
gint
sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set)
{
int rc = 0;
node_t *node1 = NULL;
node_t *node2 = NULL;
gboolean can1 = TRUE;
gboolean can2 = TRUE;
const resource_t *resource1 = (const resource_t *)a;
const resource_t *resource2 = (const resource_t *)b;
CRM_ASSERT(resource1 != NULL);
CRM_ASSERT(resource2 != NULL);
/* allocation order:
* - active instances
* - instances running on nodes with the least copies
* - active instances on nodes that can't support them or are to be fenced
* - failed instances
* - inactive instances
*/
if (resource1->running_on && resource2->running_on) {
if (g_list_length(resource1->running_on) < g_list_length(resource2->running_on)) {
crm_trace("%s < %s: running_on", resource1->id, resource2->id);
return -1;
} else if (g_list_length(resource1->running_on) > g_list_length(resource2->running_on)) {
crm_trace("%s > %s: running_on", resource1->id, resource2->id);
return 1;
}
}
if (resource1->running_on) {
node1 = resource1->running_on->data;
}
if (resource2->running_on) {
node2 = resource2->running_on->data;
}
if (node1) {
node_t *match = pe_hash_table_lookup(resource1->allowed_nodes, node1->details->id);
if (match == NULL || match->weight < 0) {
crm_trace("%s: current location is unavailable", resource1->id);
node1 = NULL;
can1 = FALSE;
}
}
if (node2) {
node_t *match = pe_hash_table_lookup(resource2->allowed_nodes, node2->details->id);
if (match == NULL || match->weight < 0) {
crm_trace("%s: current location is unavailable", resource2->id);
node2 = NULL;
can2 = FALSE;
}
}
if (can1 != can2) {
if (can1) {
crm_trace("%s < %s: availability of current location", resource1->id, resource2->id);
return -1;
}
crm_trace("%s > %s: availability of current location", resource1->id, resource2->id);
return 1;
}
if (resource1->priority < resource2->priority) {
crm_trace("%s < %s: priority", resource1->id, resource2->id);
return 1;
} else if (resource1->priority > resource2->priority) {
crm_trace("%s > %s: priority", resource1->id, resource2->id);
return -1;
}
if (node1 == NULL && node2 == NULL) {
crm_trace("%s == %s: not active", resource1->id, resource2->id);
return 0;
}
if (node1 != node2) {
if (node1 == NULL) {
crm_trace("%s > %s: active", resource1->id, resource2->id);
return 1;
} else if (node2 == NULL) {
crm_trace("%s < %s: active", resource1->id, resource2->id);
return -1;
}
}
can1 = can_run_resources(node1);
can2 = can_run_resources(node2);
if (can1 != can2) {
if (can1) {
crm_trace("%s < %s: can", resource1->id, resource2->id);
return -1;
}
crm_trace("%s > %s: can", resource1->id, resource2->id);
return 1;
}
node1 = parent_node_instance(resource1, node1);
node2 = parent_node_instance(resource2, node2);
if (node1 != NULL && node2 == NULL) {
crm_trace("%s < %s: not allowed", resource1->id, resource2->id);
return -1;
} else if (node1 == NULL && node2 != NULL) {
crm_trace("%s > %s: not allowed", resource1->id, resource2->id);
return 1;
}
if (node1 == NULL || node2 == NULL) {
crm_trace("%s == %s: not allowed", resource1->id, resource2->id);
return 0;
}
if (node1->count < node2->count) {
crm_trace("%s < %s: count", resource1->id, resource2->id);
return -1;
} else if (node1->count > node2->count) {
crm_trace("%s > %s: count", resource1->id, resource2->id);
return 1;
}
can1 = did_fail(resource1);
can2 = did_fail(resource2);
if (can1 != can2) {
if (can1) {
crm_trace("%s > %s: failed", resource1->id, resource2->id);
return 1;
}
crm_trace("%s < %s: failed", resource1->id, resource2->id);
return -1;
}
if (node1 && node2) {
int lpc = 0;
int max = 0;
node_t *n = NULL;
GListPtr gIter = NULL;
GListPtr list1 = NULL;
GListPtr list2 = NULL;
GHashTable *hash1 =
g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
GHashTable *hash2 =
g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, g_hash_destroy_str);
n = node_copy(resource1->running_on->data);
g_hash_table_insert(hash1, (gpointer) n->details->id, n);
n = node_copy(resource2->running_on->data);
g_hash_table_insert(hash2, (gpointer) n->details->id, n);
if(resource1->parent) {
for (gIter = resource1->parent->rsc_cons; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
crm_trace("Applying %s to %s", constraint->id, resource1->id);
hash1 = native_merge_weights(constraint->rsc_rh, resource1->id, hash1,
constraint->node_attribute,
(float)constraint->score / INFINITY, 0);
}
for (gIter = resource1->parent->rsc_cons_lhs; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
crm_trace("Applying %s to %s", constraint->id, resource1->id);
hash1 = native_merge_weights(constraint->rsc_lh, resource1->id, hash1,
constraint->node_attribute,
(float)constraint->score / INFINITY, pe_weights_positive);
}
}
if(resource2->parent) {
for (gIter = resource2->parent->rsc_cons; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
crm_trace("Applying %s to %s", constraint->id, resource2->id);
hash2 = native_merge_weights(constraint->rsc_rh, resource2->id, hash2,
constraint->node_attribute,
(float)constraint->score / INFINITY, 0);
}
for (gIter = resource2->parent->rsc_cons_lhs; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
crm_trace("Applying %s to %s", constraint->id, resource2->id);
hash2 = native_merge_weights(constraint->rsc_lh, resource2->id, hash2,
constraint->node_attribute,
(float)constraint->score / INFINITY, pe_weights_positive);
}
}
/* Current location score */
node1 = g_list_nth_data(resource1->running_on, 0);
node1 = g_hash_table_lookup(hash1, node1->details->id);
node2 = g_list_nth_data(resource2->running_on, 0);
node2 = g_hash_table_lookup(hash2, node2->details->id);
if (node1->weight < node2->weight) {
if (node1->weight < 0) {
crm_trace("%s > %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight);
rc = -1;
goto out;
} else {
crm_trace("%s < %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight);
rc = 1;
goto out;
}
} else if (node1->weight > node2->weight) {
crm_trace("%s > %s: current score: %d %d", resource1->id, resource2->id, node1->weight, node2->weight);
rc = -1;
goto out;
}
/* All location scores */
list1 = g_hash_table_get_values(hash1);
list2 = g_hash_table_get_values(hash2);
list1 =
g_list_sort_with_data(list1, sort_node_weight,
g_list_nth_data(resource1->running_on, 0));
list2 =
g_list_sort_with_data(list2, sort_node_weight,
g_list_nth_data(resource2->running_on, 0));
max = g_list_length(list1);
if (max < g_list_length(list2)) {
max = g_list_length(list2);
}
for (; lpc < max; lpc++) {
node1 = g_list_nth_data(list1, lpc);
node2 = g_list_nth_data(list2, lpc);
if (node1 == NULL) {
crm_trace("%s < %s: colocated score NULL", resource1->id, resource2->id);
rc = 1;
break;
} else if (node2 == NULL) {
crm_trace("%s > %s: colocated score NULL", resource1->id, resource2->id);
rc = -1;
break;
}
if (node1->weight < node2->weight) {
crm_trace("%s < %s: colocated score", resource1->id, resource2->id);
rc = 1;
break;
} else if (node1->weight > node2->weight) {
crm_trace("%s > %s: colocated score", resource1->id, resource2->id);
rc = -1;
break;
}
}
/* Order by reverse uname - same as sort_node_weight() does? */
out:
g_hash_table_destroy(hash1); /* Free mem */
g_hash_table_destroy(hash2); /* Free mem */
g_list_free(list1);
g_list_free(list2);
if (rc != 0) {
return rc;
}
}
rc = strcmp(resource1->id, resource2->id);
crm_trace("%s %c %s: default", resource1->id, rc < 0 ? '<' : '>', resource2->id);
return rc;
}
static node_t *
can_run_instance(resource_t * rsc, node_t * node, int limit)
{
node_t *local_node = NULL;
if (node == NULL && rsc->allowed_nodes) {
GHashTableIter iter;
g_hash_table_iter_init(&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next(&iter, NULL, (void **)&local_node)) {
can_run_instance(rsc, local_node, limit);
}
return NULL;
}
if (can_run_resources(node) == FALSE) {
goto bail;
} else if (is_set(rsc->flags, pe_rsc_orphan)) {
goto bail;
}
local_node = parent_node_instance(rsc, node);
if (local_node == NULL) {
crm_warn("%s cannot run on %s: node not allowed", rsc->id, node->details->uname);
goto bail;
} else if (local_node->weight < 0) {
common_update_score(rsc, node->details->id, local_node->weight);
pe_rsc_trace(rsc, "%s cannot run on %s: Parent node weight doesn't allow it.",
rsc->id, node->details->uname);
} else if (local_node->count < limit) {
pe_rsc_trace(rsc, "%s can run on %s: %d", rsc->id, node->details->uname, local_node->count);
return local_node;
} else {
pe_rsc_trace(rsc, "%s cannot run on %s: node full (%d >= %d)",
rsc->id, node->details->uname, local_node->count, limit);
}
bail:
if (node) {
common_update_score(rsc, node->details->id, -INFINITY);
}
return NULL;
}
static node_t *
color_instance(resource_t * rsc, node_t * prefer, gboolean all_coloc, int limit, pe_working_set_t * data_set)
{
node_t *chosen = NULL;
GHashTable *backup = NULL;
CRM_ASSERT(rsc);
pe_rsc_trace(rsc, "Processing %s %d %s", rsc->id, all_coloc, prefer?prefer->details->uname:"none");
if (is_not_set(rsc->flags, pe_rsc_provisional)) {
return rsc->fns->location(rsc, NULL, FALSE);
} else if (is_set(rsc->flags, pe_rsc_allocating)) {
pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
return NULL;
}
/* Only include positive colocation preferences of dependent resources
* if not every node will get a copy of the clone
*/
append_parent_colocation(rsc->parent, rsc, all_coloc);
if (prefer) {
node_t *local_prefer = g_hash_table_lookup(rsc->allowed_nodes, prefer->details->id);
if (local_prefer == NULL || local_prefer->weight < 0) {
pe_rsc_trace(rsc, "Not pre-allocating %s to %s - unavailable", rsc->id,
prefer->details->uname);
return NULL;
}
}
can_run_instance(rsc, NULL, limit);
backup = node_hash_dup(rsc->allowed_nodes);
chosen = rsc->cmds->allocate(rsc, prefer, data_set);
if (chosen) {
node_t *local_node = parent_node_instance(rsc, chosen);
if (prefer && chosen && chosen->details != prefer->details) {
crm_notice("Pre-allocation failed: got %s instead of %s",
chosen->details->uname, prefer->details->uname);
g_hash_table_destroy(rsc->allowed_nodes);
rsc->allowed_nodes = backup;
native_deallocate(rsc);
chosen = NULL;
backup = NULL;
} else if (local_node) {
local_node->count++;
} else if (is_set(rsc->flags, pe_rsc_managed)) {
/* what to do? we can't enforce per-node limits in this case */
crm_config_err("%s not found in %s (list=%d)",
chosen->details->id, rsc->parent->id,
g_hash_table_size(rsc->parent->allowed_nodes));
}
}
if(backup) {
g_hash_table_destroy(backup);
}
return chosen;
}
static void
append_parent_colocation(resource_t * rsc, resource_t * child, gboolean all)
{
GListPtr gIter = NULL;
gIter = rsc->rsc_cons;
for (; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *cons = (rsc_colocation_t *) gIter->data;
if (all || cons->score < 0 || cons->score == INFINITY) {
child->rsc_cons = g_list_prepend(child->rsc_cons, cons);
}
}
gIter = rsc->rsc_cons_lhs;
for (; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *cons = (rsc_colocation_t *) gIter->data;
if (all || cons->score < 0) {
child->rsc_cons_lhs = g_list_prepend(child->rsc_cons_lhs, cons);
}
}
}
void
distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes,
int max, int per_host_max, pe_working_set_t * data_set);
void
distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes,
int max, int per_host_max, pe_working_set_t * data_set)
{
int loop_max = 0;
int allocated = 0;
int available_nodes = 0;
/* count now tracks the number of clones currently allocated */
for(GListPtr nIter = nodes; nIter != NULL; nIter = nIter->next) {
pe_node_t *node = nIter->data;
node->count = 0;
if (can_run_resources(node)) {
available_nodes++;
}
}
if(available_nodes) {
loop_max = max / available_nodes;
}
if (loop_max < 1) {
loop_max = 1;
}
pe_rsc_debug(rsc, "Allocating %d %s instances to a possible %d nodes (%d per host, %d optimal)",
max, rsc->id, available_nodes, per_host_max, loop_max);
/* Pre-allocate as many instances as we can to their current location */
for (GListPtr gIter = children; gIter != NULL && allocated < max; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
if (child->running_on && is_set(child->flags, pe_rsc_provisional)
&& is_not_set(child->flags, pe_rsc_failed)) {
node_t *child_node = child->running_on->data;
node_t *local_node = parent_node_instance(child, child->running_on->data);
pe_rsc_trace(rsc, "Pre-allocating %s (%d remaining)", child->id, max - allocated);
pe_rsc_trace(rsc, "Foo %s to %s %d %d", child->id,
child_node->details->uname, max, available_nodes);
if (can_run_resources(child_node) == FALSE || child_node->weight < 0) {
pe_rsc_trace(rsc, "Not Pre-allocating %s", child_node->details->uname);
} else if(local_node && local_node->count >= loop_max) {
pe_rsc_trace(rsc, "Deferring allocation of %s", child_node->details->uname);
} else if (color_instance(child, child_node, max < available_nodes, per_host_max, data_set)) {
pe_rsc_trace(rsc, "Pre-allocated %s to %s", child->id,
child_node->details->uname);
allocated++;
}
}
}
pe_rsc_trace(rsc, "Done pre-allocating (%d of %d)", allocated, max);
for (GListPtr gIter = children; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
if (g_list_length(child->running_on) > 0) {
node_t *child_node = child->running_on->data;
node_t *local_node = parent_node_instance(child, child->running_on->data);
if (local_node == NULL) {
crm_err("%s is running on %s which isn't allowed",
child->id, child_node->details->uname);
}
}
if (is_not_set(child->flags, pe_rsc_provisional)) {
} else if (allocated >= max) {
pe_rsc_debug(rsc, "Child %s not allocated - limit reached %d %d", child->id, allocated, max);
resource_location(child, NULL, -INFINITY, "clone_color:limit_reached", data_set);
} else {
if (color_instance(child, NULL, max < available_nodes, per_host_max, data_set)) {
allocated++;
}
}
}
pe_rsc_debug(rsc, "Allocated %d %s instances of a possible %d",
allocated, rsc->id, max);
}
node_t *
clone_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set)
{
GListPtr nodes = NULL;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
if (is_not_set(rsc->flags, pe_rsc_provisional)) {
return NULL;
} else if (is_set(rsc->flags, pe_rsc_allocating)) {
pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
return NULL;
}
set_bit(rsc->flags, pe_rsc_allocating);
pe_rsc_trace(rsc, "Processing %s", rsc->id);
/* this information is used by sort_clone_instance() when deciding in which
* order to allocate clone instances
*/
for (GListPtr gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
pe_rsc_trace(rsc, "%s: Coloring %s first", rsc->id, constraint->rsc_rh->id);
constraint->rsc_rh->cmds->allocate(constraint->rsc_rh, prefer, data_set);
}
for (GListPtr gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
rsc->allowed_nodes =
constraint->rsc_lh->cmds->merge_weights(constraint->rsc_lh, rsc->id, rsc->allowed_nodes,
constraint->node_attribute,
(float)constraint->score / INFINITY,
(pe_weights_rollback | pe_weights_positive));
}
dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes);
nodes = g_hash_table_get_values(rsc->allowed_nodes);
nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL);
rsc->children = g_list_sort_with_data(rsc->children, sort_clone_instance, data_set);
distribute_children(rsc, rsc->children, nodes, clone_data->clone_max, clone_data->clone_node_max, data_set);
g_list_free(nodes);
clear_bit(rsc->flags, pe_rsc_provisional);
clear_bit(rsc->flags, pe_rsc_allocating);
pe_rsc_trace(rsc, "Done allocating %s", rsc->id);
return NULL;
}
static void
clone_update_pseudo_status(resource_t * rsc, gboolean * stopping, gboolean * starting,
gboolean * active)
{
GListPtr gIter = NULL;
if (rsc->children) {
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
clone_update_pseudo_status(child, stopping, starting, active);
}
return;
}
CRM_ASSERT(active != NULL);
CRM_ASSERT(starting != NULL);
CRM_ASSERT(stopping != NULL);
if (rsc->running_on) {
*active = TRUE;
}
gIter = rsc->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t *) gIter->data;
if (*starting && *stopping) {
return;
} else if (is_set(action->flags, pe_action_optional)) {
pe_rsc_trace(rsc, "Skipping optional: %s", action->uuid);
continue;
} else if (is_set(action->flags, pe_action_pseudo) == FALSE
&& is_set(action->flags, pe_action_runnable) == FALSE) {
pe_rsc_trace(rsc, "Skipping unrunnable: %s", action->uuid);
continue;
} else if (safe_str_eq(RSC_STOP, action->task)) {
pe_rsc_trace(rsc, "Stopping due to: %s", action->uuid);
*stopping = TRUE;
} else if (safe_str_eq(RSC_START, action->task)) {
if (is_set(action->flags, pe_action_runnable) == FALSE) {
pe_rsc_trace(rsc, "Skipping pseudo-op: %s run=%d, pseudo=%d",
action->uuid, is_set(action->flags, pe_action_runnable),
is_set(action->flags, pe_action_pseudo));
} else {
pe_rsc_trace(rsc, "Starting due to: %s", action->uuid);
pe_rsc_trace(rsc, "%s run=%d, pseudo=%d",
action->uuid, is_set(action->flags, pe_action_runnable),
is_set(action->flags, pe_action_pseudo));
*starting = TRUE;
}
}
}
}
static action_t *
find_rsc_action(resource_t * rsc, const char *key, gboolean active_only, GListPtr * list)
{
action_t *match = NULL;
GListPtr possible = NULL;
GListPtr active = NULL;
possible = find_actions(rsc->actions, key, NULL);
if (active_only) {
GListPtr gIter = possible;
for (; gIter != NULL; gIter = gIter->next) {
action_t *op = (action_t *) gIter->data;
if (is_set(op->flags, pe_action_optional) == FALSE) {
active = g_list_prepend(active, op);
}
}
if (active && g_list_length(active) == 1) {
match = g_list_nth_data(active, 0);
}
if (list) {
*list = active;
active = NULL;
}
} else if (possible && g_list_length(possible) == 1) {
match = g_list_nth_data(possible, 0);
}
if (list) {
*list = possible;
possible = NULL;
}
if (possible) {
g_list_free(possible);
}
if (active) {
g_list_free(active);
}
return match;
}
static void
child_ordering_constraints(resource_t * rsc, pe_working_set_t * data_set)
{
char *key = NULL;
action_t *stop = NULL;
action_t *start = NULL;
action_t *last_stop = NULL;
action_t *last_start = NULL;
GListPtr gIter = NULL;
gboolean active_only = TRUE; /* change to false to get the old behavior */
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
if (clone_data->ordered == FALSE) {
return;
}
/* we have to maintain a consistent sorted child list when building order constraints */
rsc->children = g_list_sort(rsc->children, sort_rsc_id);
for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
key = stop_key(child);
stop = find_rsc_action(child, key, active_only, NULL);
free(key);
key = start_key(child);
start = find_rsc_action(child, key, active_only, NULL);
free(key);
if (stop) {
if (last_stop) {
/* child/child relative stop */
order_actions(stop, last_stop, pe_order_optional);
}
last_stop = stop;
}
if (start) {
if (last_start) {
/* child/child relative start */
order_actions(last_start, start, pe_order_optional);
}
last_start = start;
}
}
}
void
clone_create_actions(resource_t * rsc, pe_working_set_t * data_set)
{
gboolean child_active = FALSE;
gboolean child_starting = FALSE;
gboolean child_stopping = FALSE;
gboolean allow_dependent_migrations = TRUE;
action_t *stop = NULL;
action_t *stopped = NULL;
action_t *start = NULL;
action_t *started = NULL;
GListPtr gIter = rsc->children;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
pe_rsc_trace(rsc, "Creating actions for %s", rsc->id);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
gboolean starting = FALSE;
gboolean stopping = FALSE;
child_rsc->cmds->create_actions(child_rsc, data_set);
clone_update_pseudo_status(child_rsc, &stopping, &starting, &child_active);
if (stopping && starting) {
allow_dependent_migrations = FALSE;
}
child_stopping |= stopping;
child_starting |= starting;
}
/* start */
start = start_action(rsc, NULL, !child_starting);
started = custom_action(rsc, started_key(rsc),
RSC_STARTED, NULL, !child_starting, TRUE, data_set);
update_action_flags(start, pe_action_pseudo | pe_action_runnable, __FUNCTION__, __LINE__);
update_action_flags(started, pe_action_pseudo, __FUNCTION__, __LINE__);
started->priority = INFINITY;
if (child_active || child_starting) {
update_action_flags(started, pe_action_runnable, __FUNCTION__, __LINE__);
}
child_ordering_constraints(rsc, data_set);
if (clone_data->start_notify == NULL) {
clone_data->start_notify =
create_notification_boundaries(rsc, RSC_START, start, started, data_set);
}
/* stop */
stop = stop_action(rsc, NULL, !child_stopping);
stopped = custom_action(rsc, stopped_key(rsc),
RSC_STOPPED, NULL, !child_stopping, TRUE, data_set);
stopped->priority = INFINITY;
update_action_flags(stop, pe_action_pseudo | pe_action_runnable, __FUNCTION__, __LINE__);
if (allow_dependent_migrations) {
update_action_flags(stop, pe_action_migrate_runnable, __FUNCTION__, __LINE__);
}
update_action_flags(stopped, pe_action_pseudo | pe_action_runnable, __FUNCTION__, __LINE__);
if (clone_data->stop_notify == NULL) {
clone_data->stop_notify =
create_notification_boundaries(rsc, RSC_STOP, stop, stopped, data_set);
if (clone_data->stop_notify && clone_data->start_notify) {
order_actions(clone_data->stop_notify->post_done, clone_data->start_notify->pre,
pe_order_optional);
}
}
}
void
clone_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
{
resource_t *last_rsc = NULL;
GListPtr gIter;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
pe_rsc_trace(rsc, "Internal constraints for %s", rsc->id);
new_rsc_order(rsc, RSC_STOPPED, rsc, RSC_START, pe_order_optional, data_set);
new_rsc_order(rsc, RSC_START, rsc, RSC_STARTED, pe_order_runnable_left, data_set);
new_rsc_order(rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_runnable_left, data_set);
if (rsc->variant == pe_master) {
new_rsc_order(rsc, RSC_DEMOTED, rsc, RSC_STOP, pe_order_optional, data_set);
new_rsc_order(rsc, RSC_STARTED, rsc, RSC_PROMOTE, pe_order_runnable_left, data_set);
}
if (clone_data->ordered) {
/* we have to maintain a consistent sorted child list when building order constraints */
rsc->children = g_list_sort(rsc->children, sort_rsc_id);
}
for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->internal_constraints(child_rsc, data_set);
order_start_start(rsc, child_rsc, pe_order_runnable_left | pe_order_implies_first_printed);
new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, pe_order_implies_then_printed,
data_set);
if (clone_data->ordered && last_rsc) {
order_start_start(last_rsc, child_rsc, pe_order_optional);
}
order_stop_stop(rsc, child_rsc, pe_order_implies_first_printed);
new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, pe_order_implies_then_printed,
data_set);
if (clone_data->ordered && last_rsc) {
order_stop_stop(child_rsc, last_rsc, pe_order_optional);
}
last_rsc = child_rsc;
}
}
-static bool
+bool
assign_node(resource_t * rsc, node_t * node, gboolean force)
{
bool changed = FALSE;
if (rsc->children) {
- GListPtr gIter = rsc->children;
-
- for (; gIter != NULL; gIter = gIter->next) {
+ for (GListPtr gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
- changed |= native_assign_node(child_rsc, NULL, node, force);
+ changed |= assign_node(child_rsc, node, force);
}
return changed;
}
+
if (rsc->allocated_to != NULL) {
changed = true;
}
native_assign_node(rsc, NULL, node, force);
return changed;
}
static resource_t *
find_compatible_child_by_node(resource_t * local_child, node_t * local_node, resource_t * rsc,
enum rsc_role_e filter, gboolean current)
{
- node_t *node = NULL;
GListPtr gIter = NULL;
if (local_node == NULL) {
crm_err("Can't colocate unrunnable child %s with %s", local_child->id, rsc->id);
return NULL;
}
crm_trace("Looking for compatible child from %s for %s on %s",
local_child->id, rsc->id, local_node->details->uname);
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
- enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, current);
- if (is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) {
- /* We only want instances that haven't failed */
- node = child_rsc->fns->location(child_rsc, NULL, current);
- }
-
- if (filter != RSC_ROLE_UNKNOWN && next_role != filter) {
- crm_trace("Filtered %s", child_rsc->id);
- continue;
- }
-
- if (node && local_node && node->details == local_node->details) {
+ if(is_child_compatible(child_rsc, local_node, filter, current)) {
crm_trace("Pairing %s with %s on %s",
- local_child->id, child_rsc->id, node->details->uname);
+ local_child->id, child_rsc->id, local_node->details->uname);
return child_rsc;
-
- } else if (node) {
- crm_trace("%s - %s vs %s", child_rsc->id, node->details->uname,
- local_node->details->uname);
-
- } else {
- crm_trace("%s - not allocated %d", child_rsc->id, current);
}
}
crm_trace("Can't pair %s with %s", local_child->id, rsc->id);
return NULL;
}
+gboolean
+is_child_compatible(resource_t *child_rsc, node_t * local_node, enum rsc_role_e filter, gboolean current)
+{
+ node_t *node = NULL;
+ enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, current);
+
+ if (is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) {
+ /* We only want instances that haven't failed */
+ node = child_rsc->fns->location(child_rsc, NULL, current);
+ }
+
+ if (filter != RSC_ROLE_UNKNOWN && next_role != filter) {
+ crm_trace("Filtered %s", child_rsc->id);
+ return FALSE;
+ }
+
+ if (node && local_node && node->details == local_node->details) {
+ return TRUE;
+
+ } else if (node) {
+ crm_trace("%s - %s vs %s", child_rsc->id, node->details->uname,
+ local_node->details->uname);
+
+ } else {
+ crm_trace("%s - not allocated %d", child_rsc->id, current);
+ }
+ return FALSE;
+}
+
resource_t *
find_compatible_child(resource_t * local_child, resource_t * rsc, enum rsc_role_e filter,
gboolean current)
{
resource_t *pair = NULL;
GListPtr gIter = NULL;
GListPtr scratch = NULL;
node_t *local_node = NULL;
local_node = local_child->fns->location(local_child, NULL, current);
if (local_node) {
return find_compatible_child_by_node(local_child, local_node, rsc, filter, current);
}
scratch = g_hash_table_get_values(local_child->allowed_nodes);
scratch = g_list_sort_with_data(scratch, sort_node_weight, NULL);
gIter = scratch;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
pair = find_compatible_child_by_node(local_child, node, rsc, filter, current);
if (pair) {
goto done;
}
}
pe_rsc_debug(rsc, "Can't pair %s with %s", local_child->id, rsc->id);
done:
g_list_free(scratch);
return pair;
}
void
clone_rsc_colocation_lh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
{
/* -- Never called --
*
* Instead we add the colocation constraints to the child and call from there
*/
GListPtr gIter = rsc_lh->children;
CRM_CHECK(FALSE, crm_err("This functionality is not thought to be used. Please report a bug."));
CRM_CHECK(rsc_lh, return);
CRM_CHECK(rsc_rh, return);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->rsc_colocation_lh(child_rsc, rsc_rh, constraint);
}
return;
}
void
clone_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
{
GListPtr gIter = NULL;
gboolean do_interleave = FALSE;
clone_variant_data_t *clone_data = NULL;
clone_variant_data_t *clone_data_lh = NULL;
CRM_CHECK(constraint != NULL, return);
CRM_CHECK(rsc_lh != NULL, pe_err("rsc_lh was NULL for %s", constraint->id); return);
CRM_CHECK(rsc_rh != NULL, pe_err("rsc_rh was NULL for %s", constraint->id); return);
CRM_CHECK(rsc_lh->variant == pe_native, return);
get_clone_variant_data(clone_data, constraint->rsc_rh);
pe_rsc_trace(rsc_rh, "Processing constraint %s: %s -> %s %d",
constraint->id, rsc_lh->id, rsc_rh->id, constraint->score);
if (pe_rsc_is_clone(constraint->rsc_lh)) {
get_clone_variant_data(clone_data_lh, constraint->rsc_lh);
if (clone_data_lh->interleave
&& clone_data->clone_node_max != clone_data_lh->clone_node_max) {
crm_config_err("Cannot interleave " XML_CIB_TAG_INCARNATION " %s and %s because"
" they do not support the same number of" " resources per node",
constraint->rsc_lh->id, constraint->rsc_rh->id);
/* only the LHS side needs to be labeled as interleave */
} else if (clone_data_lh->interleave) {
do_interleave = TRUE;
}
}
if (is_set(rsc_rh->flags, pe_rsc_provisional)) {
pe_rsc_trace(rsc_rh, "%s is still provisional", rsc_rh->id);
return;
} else if (do_interleave) {
resource_t *rh_child = NULL;
rh_child = find_compatible_child(rsc_lh, rsc_rh, RSC_ROLE_UNKNOWN, FALSE);
if (rh_child) {
pe_rsc_debug(rsc_rh, "Pairing %s with %s", rsc_lh->id, rh_child->id);
rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint);
} else if (constraint->score >= INFINITY) {
crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id);
assign_node(rsc_lh, NULL, TRUE);
} else {
pe_rsc_debug(rsc_rh, "Cannot pair %s with instance of %s", rsc_lh->id, rsc_rh->id);
}
return;
} else if (constraint->score >= INFINITY) {
GListPtr rhs = NULL;
gIter = rsc_rh->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
if (chosen != NULL && is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) {
pe_rsc_trace(rsc_rh, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight);
rhs = g_list_prepend(rhs, chosen);
}
}
node_list_exclude(rsc_lh->allowed_nodes, rhs, FALSE);
g_list_free(rhs);
return;
}
gIter = rsc_rh->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->rsc_colocation_rh(rsc_lh, child_rsc, constraint);
}
}
static enum action_tasks
clone_child_action(action_t * action)
{
enum action_tasks result = no_action;
resource_t *child = (resource_t *) action->rsc->children->data;
if (safe_str_eq(action->task, "notify")
|| safe_str_eq(action->task, "notified")) {
/* Find the action we're notifying about instead */
int stop = 0;
char *key = action->uuid;
int lpc = strlen(key);
for (; lpc > 0; lpc--) {
if (key[lpc] == '_' && stop == 0) {
stop = lpc;
} else if (key[lpc] == '_') {
char *task_mutable = NULL;
lpc++;
task_mutable = strdup(key + lpc);
task_mutable[stop - lpc] = 0;
crm_trace("Extracted action '%s' from '%s'", task_mutable, key);
result = get_complex_task(child, task_mutable, TRUE);
free(task_mutable);
break;
}
}
} else {
result = get_complex_task(child, action->task, TRUE);
}
return result;
}
enum pe_action_flags
clone_action_flags(action_t * action, node_t * node)
{
GListPtr gIter = NULL;
gboolean any_runnable = FALSE;
gboolean check_runnable = TRUE;
enum action_tasks task = clone_child_action(action);
enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
const char *task_s = task2text(task);
gIter = action->rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
action_t *child_action = NULL;
resource_t *child = (resource_t *) gIter->data;
child_action =
find_first_action(child->actions, NULL, task_s, child->children ? NULL : node);
pe_rsc_trace(action->rsc, "Checking for %s in %s on %s", task_s, child->id,
node ? node->details->uname : "none");
if (child_action) {
enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node);
if (is_set(flags, pe_action_optional)
&& is_set(child_flags, pe_action_optional) == FALSE) {
pe_rsc_trace(child, "%s is mandatory because of %s", action->uuid,
child_action->uuid);
flags = crm_clear_bit(__FUNCTION__, __LINE__, action->rsc->id, flags, pe_action_optional);
pe_clear_action_bit(action, pe_action_optional);
}
if (is_set(child_flags, pe_action_runnable)) {
any_runnable = TRUE;
}
} else {
GListPtr gIter2 = child->actions;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
action_t *op = (action_t *) gIter2->data;
pe_rsc_trace(child, "%s on %s (%s)", op->uuid,
op->node ? op->node->details->uname : "none", op->task);
}
}
}
if (check_runnable && any_runnable == FALSE) {
pe_rsc_trace(action->rsc, "%s is not runnable because no children are", action->uuid);
flags = crm_clear_bit(__FUNCTION__, __LINE__, action->rsc->id, flags, pe_action_runnable);
if (node == NULL) {
pe_clear_action_bit(action, pe_action_runnable);
}
}
return flags;
}
static enum pe_graph_flags
clone_update_actions_interleave(action_t * first, action_t * then, node_t * node,
enum pe_action_flags flags, enum pe_action_flags filter,
enum pe_ordering type)
{
gboolean current = FALSE;
resource_t *first_child = NULL;
GListPtr gIter = then->rsc->children;
enum pe_graph_flags changed = pe_graph_none; /*pe_graph_disable */
enum action_tasks task = clone_child_action(first);
const char *first_task = task2text(task);
/* Fix this - lazy */
if (crm_ends_with(first->uuid, "_stopped_0")
|| crm_ends_with(first->uuid, "_demoted_0")) {
current = TRUE;
}
for (; gIter != NULL; gIter = gIter->next) {
resource_t *then_child = (resource_t *) gIter->data;
CRM_ASSERT(then_child != NULL);
first_child = find_compatible_child(then_child, first->rsc, RSC_ROLE_UNKNOWN, current);
if (first_child == NULL && current) {
crm_trace("Ignore");
} else if (first_child == NULL) {
crm_debug("No match found for %s (%d / %s / %s)", then_child->id, current, first->uuid,
then->uuid);
/* Me no like this hack - but what else can we do?
*
* If there is no-one active or about to be active
* on the same node as then_child, then they must
* not be allowed to start
*/
if (type & (pe_order_runnable_left | pe_order_implies_then) /* Mandatory */ ) {
pe_rsc_info(then->rsc, "Inhibiting %s from being active", then_child->id);
if(assign_node(then_child, NULL, TRUE)) {
changed |= pe_graph_updated_then;
}
}
} else {
action_t *first_action = NULL;
action_t *then_action = NULL;
pe_rsc_debug(then->rsc, "Pairing %s with %s", first_child->id, then_child->id);
first_action = find_first_action(first_child->actions, NULL, first_task, node);
then_action = find_first_action(then_child->actions, NULL, then->task, node);
if (first_action == NULL) {
if (is_not_set(first_child->flags, pe_rsc_orphan)
&& crm_str_eq(first_task, RSC_STOP, TRUE) == FALSE
&& crm_str_eq(first_task, RSC_DEMOTE, TRUE) == FALSE) {
crm_err("Internal error: No action found for %s in %s (first)",
first_task, first_child->id);
} else {
crm_trace("No action found for %s in %s%s (first)",
first_task, first_child->id,
is_set(first_child->flags, pe_rsc_orphan) ? " (ORPHAN)" : "");
}
continue;
}
/* We're only interested if 'then' is neither stopping nor being demoted */
if (then_action == NULL) {
if (is_not_set(then_child->flags, pe_rsc_orphan)
&& crm_str_eq(then->task, RSC_STOP, TRUE) == FALSE
&& crm_str_eq(then->task, RSC_DEMOTE, TRUE) == FALSE) {
crm_err("Internal error: No action found for %s in %s (then)",
then->task, then_child->id);
} else {
crm_trace("No action found for %s in %s%s (then)",
then->task, then_child->id,
is_set(then_child->flags, pe_rsc_orphan) ? " (ORPHAN)" : "");
}
continue;
}
if (order_actions(first_action, then_action, type)) {
crm_debug("Created constraint for %s -> %s", first_action->uuid, then_action->uuid);
changed |= (pe_graph_updated_first | pe_graph_updated_then);
}
changed |=
then_child->cmds->update_actions(first_action, then_action, node,
first_child->cmds->action_flags(first_action,
node), filter,
type);
}
}
return changed;
}
enum pe_graph_flags
clone_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags,
enum pe_action_flags filter, enum pe_ordering type)
{
const char *rsc = "none";
gboolean interleave = FALSE;
enum pe_graph_flags changed = pe_graph_none;
if (first->rsc != then->rsc
&& pe_rsc_is_clone(first->rsc)
&& pe_rsc_is_clone(then->rsc)) {
clone_variant_data_t *clone_data = NULL;
if (crm_ends_with(then->uuid, "_stop_0")
|| crm_ends_with(then->uuid, "_demote_0")) {
get_clone_variant_data(clone_data, first->rsc);
rsc = first->rsc->id;
} else {
get_clone_variant_data(clone_data, then->rsc);
rsc = then->rsc->id;
}
interleave = clone_data->interleave;
}
crm_trace("Interleave %s -> %s: %s (based on %s)",
first->uuid, then->uuid, interleave ? "yes" : "no", rsc);
if (interleave) {
changed = clone_update_actions_interleave(first, then, node, flags, filter, type);
} else if (then->rsc) {
GListPtr gIter = then->rsc->children;
changed |= native_update_actions(first, then, node, flags, filter, type);
for (; gIter != NULL; gIter = gIter->next) {
enum pe_graph_flags child_changed = pe_graph_none;
GListPtr lpc = NULL;
resource_t *child = (resource_t *) gIter->data;
action_t *child_action = find_first_action(child->actions, NULL, then->task, node);
if (child_action) {
enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node);
if (is_set(child_flags, pe_action_runnable)) {
child_changed |=
child->cmds->update_actions(first, child_action, node, flags, filter, type);
}
changed |= child_changed;
if (child_changed & pe_graph_updated_then) {
for (lpc = child_action->actions_after; lpc != NULL; lpc = lpc->next) {
action_wrapper_t *other = (action_wrapper_t *) lpc->data;
update_action(other->action);
}
}
}
}
}
return changed;
}
void
clone_rsc_location(resource_t * rsc, rsc_to_node_t * constraint)
{
GListPtr gIter = rsc->children;
pe_rsc_trace(rsc, "Processing location constraint %s for %s", constraint->id, rsc->id);
native_rsc_location(rsc, constraint);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->rsc_location(child_rsc, constraint);
}
}
void
clone_expand(resource_t * rsc, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
gIter = rsc->actions;
for (; gIter != NULL; gIter = gIter->next) {
action_t *op = (action_t *) gIter->data;
rsc->cmds->action_flags(op, NULL);
}
if (clone_data->start_notify) {
collect_notification_data(rsc, TRUE, TRUE, clone_data->start_notify);
expand_notification_data(clone_data->start_notify, data_set);
create_notifications(rsc, clone_data->start_notify, data_set);
}
if (clone_data->stop_notify) {
collect_notification_data(rsc, TRUE, TRUE, clone_data->stop_notify);
expand_notification_data(clone_data->stop_notify, data_set);
create_notifications(rsc, clone_data->stop_notify, data_set);
}
if (clone_data->promote_notify) {
collect_notification_data(rsc, TRUE, TRUE, clone_data->promote_notify);
expand_notification_data(clone_data->promote_notify, data_set);
create_notifications(rsc, clone_data->promote_notify, data_set);
}
if (clone_data->demote_notify) {
collect_notification_data(rsc, TRUE, TRUE, clone_data->demote_notify);
expand_notification_data(clone_data->demote_notify, data_set);
create_notifications(rsc, clone_data->demote_notify, data_set);
}
/* Now that the notifcations have been created we can expand the children */
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->expand(child_rsc, data_set);
}
native_expand(rsc, data_set);
/* The notifications are in the graph now, we can destroy the notify_data */
free_notification_data(clone_data->demote_notify);
clone_data->demote_notify = NULL;
free_notification_data(clone_data->stop_notify);
clone_data->stop_notify = NULL;
free_notification_data(clone_data->start_notify);
clone_data->start_notify = NULL;
free_notification_data(clone_data->promote_notify);
clone_data->promote_notify = NULL;
}
node_t *
rsc_known_on(resource_t * rsc, GListPtr * list)
{
GListPtr gIter = NULL;
node_t *one = NULL;
GListPtr result = NULL;
if (rsc->children) {
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t *) gIter->data;
rsc_known_on(child, &result);
}
} else if (rsc->known_on) {
result = g_hash_table_get_values(rsc->known_on);
}
if (result && g_list_length(result) == 1) {
one = g_list_nth_data(result, 0);
}
if (list) {
GListPtr gIter = NULL;
gIter = result;
for (; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t *) gIter->data;
if (*list == NULL || pe_find_node_id(*list, node->details->id) == NULL) {
*list = g_list_prepend(*list, node);
}
}
}
g_list_free(result);
return one;
}
static resource_t *
find_instance_on(resource_t * rsc, node_t * node)
{
GListPtr gIter = NULL;
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
GListPtr gIter2 = NULL;
GListPtr known_list = NULL;
resource_t *child = (resource_t *) gIter->data;
rsc_known_on(child, &known_list);
gIter2 = known_list;
for (; gIter2 != NULL; gIter2 = gIter2->next) {
node_t *known = (node_t *) gIter2->data;
if (node->details == known->details) {
g_list_free(known_list);
return child;
}
}
g_list_free(known_list);
}
return NULL;
}
gboolean
clone_create_probe(resource_t * rsc, node_t * node, action_t * complete,
gboolean force, pe_working_set_t * data_set)
{
GListPtr gIter = NULL;
gboolean any_created = FALSE;
clone_variant_data_t *clone_data = NULL;
CRM_ASSERT(rsc);
get_clone_variant_data(clone_data, rsc);
rsc->children = g_list_sort(rsc->children, sort_rsc_id);
if (rsc->children == NULL) {
pe_warn("Clone %s has no children", rsc->id);
return FALSE;
}
if (rsc->exclusive_discover) {
node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes, node->details->id);
if (allowed && allowed->rsc_discover_mode != discover_exclusive) {
/* exclusive discover is enabled and this node is not marked
* as a node this resource should be discovered on
*
* remove the node from allowed_nodes so that the
* notification contains only nodes that we might ever run
* on
*/
g_hash_table_remove(rsc->allowed_nodes, node->details->id);
/* Bit of a shortcut - might as well take it */
return FALSE;
}
}
if (is_not_set(rsc->flags, pe_rsc_unique)
&& clone_data->clone_node_max == 1) {
/* only look for one copy */
resource_t *child = NULL;
/* Try whoever we probed last time */
child = find_instance_on(rsc, node);
if (child) {
return child->cmds->create_probe(child, node, complete, force, data_set);
}
/* Try whoever we plan on starting there */
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
node_t *local_node = NULL;
resource_t *child_rsc = (resource_t *) gIter->data;
CRM_ASSERT(child_rsc);
local_node = child_rsc->fns->location(child_rsc, NULL, FALSE);
if (local_node == NULL) {
continue;
}
if (local_node->details == node->details) {
return child_rsc->cmds->create_probe(child_rsc, node, complete, force, data_set);
}
}
/* Fall back to the first clone instance */
CRM_ASSERT(rsc->children);
child = rsc->children->data;
return child->cmds->create_probe(child, node, complete, force, data_set);
}
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
if (child_rsc->cmds->create_probe(child_rsc, node, complete, force, data_set)) {
any_created = TRUE;
}
if (any_created && is_not_set(rsc->flags, pe_rsc_unique)
&& clone_data->clone_node_max == 1) {
/* only look for one copy (clone :0) */
break;
}
}
return any_created;
}
void
clone_append_meta(resource_t * rsc, xmlNode * xml)
{
char *name = NULL;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
name = crm_meta_name(XML_RSC_ATTR_UNIQUE);
crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_unique) ? "true" : "false");
free(name);
name = crm_meta_name(XML_RSC_ATTR_NOTIFY);
crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_notify) ? "true" : "false");
free(name);
name = crm_meta_name(XML_RSC_ATTR_INCARNATION_MAX);
crm_xml_add_int(xml, name, clone_data->clone_max);
free(name);
name = crm_meta_name(XML_RSC_ATTR_INCARNATION_NODEMAX);
crm_xml_add_int(xml, name, clone_data->clone_node_max);
free(name);
}
GHashTable *
clone_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr,
float factor, enum pe_weights flags)
{
return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags);
}
diff --git a/pengine/container.c b/pengine/container.c
index 3dc1b35198..67b0052057 100644
--- a/pengine/container.c
+++ b/pengine/container.c
@@ -1,416 +1,487 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* 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 <notif.h>
#include <utils.h>
#define VARIANT_CONTAINER 1
#include <lib/pengine/variant.h>
static bool
is_child_container_node(container_variant_data_t *data, pe_node_t *node)
{
for (GListPtr gIter = data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
if(node->details == tuple->node->details) {
return TRUE;
}
}
return FALSE;
}
gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
void distribute_children(resource_t *rsc, GListPtr children, GListPtr nodes,
int max, int per_host_max, pe_working_set_t * data_set);
node_t *
container_color(resource_t * rsc, node_t * prefer, pe_working_set_t * data_set)
{
GListPtr containers = NULL;
GListPtr nodes = NULL;
container_variant_data_t *container_data = NULL;
CRM_CHECK(rsc != NULL, return NULL);
get_container_variant_data(container_data, rsc);
set_bit(rsc->flags, pe_rsc_allocating);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
containers = g_list_append(containers, tuple->docker);
}
dump_node_scores(show_scores ? 0 : scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes);
nodes = g_hash_table_get_values(rsc->allowed_nodes);
nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL);
containers = g_list_sort_with_data(containers, sort_clone_instance, data_set);
distribute_children(rsc, containers, nodes,
container_data->replicas, container_data->replicas_per_host, data_set);
g_list_free(nodes);
g_list_free(containers);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
CRM_ASSERT(tuple);
if(tuple->ip) {
tuple->ip->cmds->allocate(tuple->ip, prefer, data_set);
}
if(tuple->remote) {
tuple->remote->cmds->allocate(tuple->remote, prefer, data_set);
}
// Explicitly allocate tuple->child before the container->child
if(tuple->child) {
pe_node_t *node = NULL;
GHashTableIter iter;
g_hash_table_iter_init(&iter, tuple->child->allowed_nodes);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
if(node->details != tuple->node->details) {
node->weight = -INFINITY;
} else {
node->weight = INFINITY;
}
}
set_bit(tuple->child->parent->flags, pe_rsc_allocating);
tuple->child->cmds->allocate(tuple->child, tuple->node, data_set);
clear_bit(tuple->child->parent->flags, pe_rsc_allocating);
}
}
if(container_data->child) {
pe_node_t *node = NULL;
GHashTableIter iter;
g_hash_table_iter_init(&iter, container_data->child->allowed_nodes);
while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
if(is_child_container_node(container_data, node)) {
node->weight = 0;
} else {
node->weight = -INFINITY;
}
}
container_data->child->cmds->allocate(container_data->child, prefer, data_set);
}
clear_bit(rsc->flags, pe_rsc_allocating);
clear_bit(rsc->flags, pe_rsc_provisional);
return NULL;
}
void
container_create_actions(resource_t * rsc, pe_working_set_t * data_set)
{
container_variant_data_t *container_data = NULL;
CRM_CHECK(rsc != NULL, return);
get_container_variant_data(container_data, rsc);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
CRM_ASSERT(tuple);
if(tuple->ip) {
tuple->ip->cmds->create_actions(tuple->ip, data_set);
}
if(tuple->docker) {
tuple->docker->cmds->create_actions(tuple->docker, data_set);
}
if(tuple->remote) {
tuple->remote->cmds->create_actions(tuple->remote, data_set);
}
}
if(container_data->child) {
container_data->child->cmds->create_actions(container_data->child, data_set);
}
}
void
container_internal_constraints(resource_t * rsc, pe_working_set_t * data_set)
{
container_variant_data_t *container_data = NULL;
CRM_CHECK(rsc != NULL, return);
get_container_variant_data(container_data, rsc);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
CRM_ASSERT(tuple);
if(tuple->docker) {
tuple->docker->cmds->internal_constraints(tuple->docker, data_set);
}
if(tuple->ip) {
tuple->ip->cmds->internal_constraints(tuple->ip, data_set);
// Start ip then docker
new_rsc_order(tuple->ip, RSC_START, tuple->docker, RSC_START,
pe_order_runnable_left|pe_order_preserve, data_set);
new_rsc_order(tuple->docker, RSC_STOP, tuple->ip, RSC_STOP,
pe_order_implies_first|pe_order_preserve, data_set);
rsc_colocation_new("ip-with-docker", NULL, INFINITY, tuple->ip, tuple->docker, NULL, NULL, data_set);
}
if(tuple->remote) {
/* This handles ordering and colocating remote relative to docker
* (via "resource-with-container"). Since IP is also ordered and
* colocated relative to docker, we don't need to do anything
* explicit here with IP.
*/
tuple->remote->cmds->internal_constraints(tuple->remote, data_set);
}
if(tuple->child) {
CRM_ASSERT(tuple->remote);
// Start of the remote then child is implicit in the PE's remote logic
}
}
if(container_data->child) {
container_data->child->cmds->internal_constraints(container_data->child, data_set);
}
}
+
+static resource_t *
+find_compatible_tuple_by_node(resource_t * rsc_lh, node_t * candidate, resource_t * rsc,
+ enum rsc_role_e filter, gboolean current)
+{
+ container_variant_data_t *container_data = NULL;
+
+ CRM_CHECK(candidate != NULL, return NULL);
+ get_container_variant_data(container_data, rsc);
+
+ crm_trace("Looking for compatible child from %s for %s on %s",
+ rsc_lh->id, rsc->id, candidate->details->uname);
+
+ for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
+ container_grouping_t *tuple = (container_grouping_t *)gIter->data;
+
+ if(is_child_compatible(tuple->docker, candidate, filter, current)) {
+ crm_trace("Pairing %s with %s on %s",
+ rsc_lh->id, tuple->docker->id, candidate->details->uname);
+ return tuple->docker;
+ }
+ }
+
+ crm_trace("Can't pair %s with %s", rsc_lh->id, rsc->id);
+ return NULL;
+}
+
+static resource_t *
+find_compatible_tuple(resource_t *rsc_lh, resource_t * rsc, enum rsc_role_e filter,
+ gboolean current)
+{
+ GListPtr scratch = NULL;
+ resource_t *pair = NULL;
+ node_t *active_node_lh = NULL;
+
+ active_node_lh = rsc_lh->fns->location(rsc_lh, NULL, current);
+ if (active_node_lh) {
+ return find_compatible_tuple_by_node(rsc_lh, active_node_lh, rsc, filter, current);
+ }
+
+ scratch = g_hash_table_get_values(rsc_lh->allowed_nodes);
+ scratch = g_list_sort_with_data(scratch, sort_node_weight, NULL);
+
+ for (GListPtr gIter = scratch; gIter != NULL; gIter = gIter->next) {
+ node_t *node = (node_t *) gIter->data;
+
+ pair = find_compatible_tuple_by_node(rsc_lh, node, rsc, filter, current);
+ if (pair) {
+ goto done;
+ }
+ }
+
+ pe_rsc_debug(rsc, "Can't pair %s with %s", rsc_lh->id, rsc->id);
+ done:
+ g_list_free(scratch);
+ return pair;
+}
+
void
-container_rsc_colocation_lh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
+container_rsc_colocation_lh(resource_t * rsc, resource_t * rsc_rh, rsc_colocation_t * constraint)
{
- pe_err("Container %s cannot be colocated with anything", rsc_lh->id);
+ pe_err("Container %s cannot be colocated with anything", rsc->id);
}
void
-container_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc_rh, rsc_colocation_t * constraint)
+container_rsc_colocation_rh(resource_t * rsc_lh, resource_t * rsc, rsc_colocation_t * constraint)
{
GListPtr allocated_rhs = NULL;
container_variant_data_t *container_data = NULL;
CRM_CHECK(constraint != NULL, return);
CRM_CHECK(rsc_lh != NULL, pe_err("rsc_lh was NULL for %s", constraint->id); return);
- CRM_CHECK(rsc_rh != NULL, pe_err("rsc_rh was NULL for %s", constraint->id); return);
+ CRM_CHECK(rsc != NULL, pe_err("rsc was NULL for %s", constraint->id); return);
- if (is_set(rsc_rh->flags, pe_rsc_provisional)) {
- pe_rsc_trace(rsc_rh, "%s is still provisional", rsc_rh->id);
+ if (is_set(rsc->flags, pe_rsc_provisional)) {
+ pe_rsc_trace(rsc, "%s is still provisional", rsc->id);
return;
- } else if(rsc_lh->variant > pe_group) {
- pe_err("Only basic resources and groups can be colocated with %s", rsc_rh->id);
+ } else if(constraint->rsc_lh->variant > pe_group) {
+ resource_t *rh_child = find_compatible_tuple(rsc_lh, rsc, RSC_ROLE_UNKNOWN, FALSE);
+
+ if (rh_child) {
+ pe_rsc_debug(rsc, "Pairing %s with %s", rsc_lh->id, rh_child->id);
+ rsc_lh->cmds->rsc_colocation_lh(rsc_lh, rh_child, constraint);
+
+ } else if (constraint->score >= INFINITY) {
+ crm_notice("Cannot pair %s with instance of %s", rsc_lh->id, rsc->id);
+ assign_node(rsc_lh, NULL, TRUE);
+
+ } else {
+ pe_rsc_debug(rsc, "Cannot pair %s with instance of %s", rsc_lh->id, rsc->id);
+ }
+
return;
}
- get_container_variant_data(container_data, constraint->rsc_rh);
- pe_rsc_trace(rsc_rh, "Processing constraint %s: %s -> %s %d",
- constraint->id, rsc_lh->id, rsc_rh->id, constraint->score);
+ get_container_variant_data(container_data, rsc);
+ pe_rsc_trace(rsc, "Processing constraint %s: %s -> %s %d",
+ constraint->id, rsc_lh->id, rsc->id, constraint->score);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
if (constraint->score < INFINITY) {
tuple->docker->cmds->rsc_colocation_rh(rsc_lh, tuple->docker, constraint);
} else {
node_t *chosen = tuple->docker->fns->location(tuple->docker, NULL, FALSE);
if (chosen != NULL && is_set_recursive(tuple->docker, pe_rsc_block, TRUE) == FALSE) {
- pe_rsc_trace(rsc_rh, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight);
+ pe_rsc_trace(rsc, "Allowing %s: %s %d", constraint->id, chosen->details->uname, chosen->weight);
allocated_rhs = g_list_prepend(allocated_rhs, chosen);
}
}
}
if (constraint->score >= INFINITY) {
node_list_exclude(rsc_lh->allowed_nodes, allocated_rhs, FALSE);
}
g_list_free(allocated_rhs);
}
enum pe_action_flags
container_action_flags(action_t * action, node_t * node)
{
enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
return flags;
}
enum pe_graph_flags
container_update_actions(action_t * first, action_t * then, node_t * node, enum pe_action_flags flags,
enum pe_action_flags filter, enum pe_ordering type)
{
enum pe_graph_flags changed = pe_graph_none;
return changed;
}
void
container_rsc_location(resource_t * rsc, rsc_to_node_t * constraint)
{
GListPtr gIter = rsc->children;
pe_rsc_trace(rsc, "Processing location constraint %s for %s", constraint->id, rsc->id);
native_rsc_location(rsc, constraint);
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->rsc_location(child_rsc, constraint);
}
}
void
container_expand(resource_t * rsc, pe_working_set_t * data_set)
{
container_variant_data_t *container_data = NULL;
CRM_CHECK(rsc != NULL, return);
get_container_variant_data(container_data, rsc);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
CRM_ASSERT(tuple);
if (tuple->docker && tuple->remote && tuple->docker->allocated_to
&& fix_remote_addr(tuple->remote)) {
// REMOTE_CONTAINER_HACK: Allow remote nodes that start containers with pacemaker remote inside
xmlNode *nvpair = get_xpath_object("//nvpair[@name='addr']", tuple->remote->xml, LOG_ERR);
g_hash_table_replace(tuple->remote->parameters, strdup("addr"), strdup(tuple->docker->allocated_to->details->uname));
crm_xml_add(nvpair, "value", tuple->docker->allocated_to->details->uname);
}
if(tuple->ip) {
tuple->ip->cmds->expand(tuple->ip, data_set);
}
if(tuple->child) {
tuple->child->cmds->expand(tuple->child, data_set);
}
if(tuple->docker) {
tuple->docker->cmds->expand(tuple->docker, data_set);
}
if(tuple->remote) {
tuple->remote->cmds->expand(tuple->remote, data_set);
}
}
}
gboolean
container_create_probe(resource_t * rsc, node_t * node, action_t * complete,
gboolean force, pe_working_set_t * data_set)
{
bool any_created = FALSE;
container_variant_data_t *container_data = NULL;
CRM_CHECK(rsc != NULL, return FALSE);
get_container_variant_data(container_data, rsc);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
CRM_ASSERT(tuple);
if(tuple->ip) {
any_created |= tuple->ip->cmds->create_probe(tuple->ip, node, complete, force, data_set);
}
if(tuple->child && node->details == tuple->node->details) {
any_created |= tuple->child->cmds->create_probe(tuple->child, node, complete, force, data_set);
}
if(tuple->docker) {
bool created = tuple->docker->cmds->create_probe(tuple->docker, node, complete, force, data_set);
if(created) {
any_created = TRUE;
/* If we're limited to one replica per host (due to
* the lack of an IP range probably), then we don't
* want any of our peer containers starting until
* we've established that no other copies are already
* running.
*
* Partly this is to ensure that replicas_per_host is
* observed, but also to ensure that the containers
* don't fail to start because the necessary port
* mappings (which won't include an IP for uniqueness)
* are already taken
*/
for (GListPtr tIter = container_data->tuples; tIter != NULL && container_data->replicas_per_host == 1; tIter = tIter->next) {
container_grouping_t *other = (container_grouping_t *)tIter->data;
if ((other != tuple) && (other != NULL)
&& (other->docker != NULL)) {
custom_action_order(tuple->docker, generate_op_key(tuple->docker->id, RSC_STATUS, 0), NULL,
other->docker, generate_op_key(other->docker->id, RSC_START, 0), NULL,
pe_order_optional, data_set);
}
}
}
}
if(FALSE && tuple->remote) {
// TODO: Needed?
any_created |= tuple->remote->cmds->create_probe(tuple->remote, node, complete, force, data_set);
}
}
return any_created;
}
void
container_append_meta(resource_t * rsc, xmlNode * xml)
{
}
GHashTable *
container_merge_weights(resource_t * rsc, const char *rhs, GHashTable * nodes, const char *attr,
float factor, enum pe_weights flags)
{
return rsc_merge_weights(rsc, rhs, nodes, attr, factor, flags);
}
void container_LogActions(
resource_t * rsc, pe_working_set_t * data_set, gboolean terminal)
{
container_variant_data_t *container_data = NULL;
CRM_CHECK(rsc != NULL, return);
get_container_variant_data(container_data, rsc);
for (GListPtr gIter = container_data->tuples; gIter != NULL; gIter = gIter->next) {
container_grouping_t *tuple = (container_grouping_t *)gIter->data;
CRM_ASSERT(tuple);
if(tuple->ip) {
LogActions(tuple->ip, data_set, terminal);
}
if(tuple->docker) {
LogActions(tuple->docker, data_set, terminal);
}
if(tuple->remote) {
LogActions(tuple->remote, data_set, terminal);
}
if(tuple->child) {
LogActions(tuple->child, data_set, terminal);
}
}
}
diff --git a/pengine/utils.h b/pengine/utils.h
index fc503be663..79fd33d0e4 100644
--- a/pengine/utils.h
+++ b/pengine/utils.h
@@ -1,78 +1,80 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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 Lesser 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
*/
#ifndef PENGINE_AUTILS__H
# define PENGINE_AUTILS__H
/* Constraint helper functions */
extern rsc_colocation_t *invert_constraint(rsc_colocation_t * constraint);
extern rsc_to_node_t *copy_constraint(rsc_to_node_t * constraint);
extern rsc_to_node_t *rsc2node_new(const char *id, resource_t * rsc, int weight,
const char *discovery_mode, node_t * node,
pe_working_set_t * data_set);
extern void pe_free_rsc_to_node(GListPtr constraints);
extern void pe_free_ordering(GListPtr constraints);
extern gboolean rsc_colocation_new(const char *id, const char *node_attr, int score,
resource_t * rsc_lh, resource_t * rsc_rh,
const char *state_lh, const char *state_rh,
pe_working_set_t * data_set);
extern gboolean rsc_ticket_new(const char *id, resource_t * rsc_lh, ticket_t * ticket,
const char *state_lh, const char *loss_policy,
pe_working_set_t * data_set);
extern gint sort_node_weight(gconstpointer a, gconstpointer b, gpointer data_set);
extern gboolean can_run_resources(const node_t * node);
extern gboolean native_assign_node(resource_t * rsc, GListPtr candidates, node_t * chosen,
gboolean force);
void native_deallocate(resource_t * rsc);
extern void log_action(unsigned int log_level, const char *pre_text,
action_t * action, gboolean details);
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);
+gboolean is_child_compatible(resource_t *child_rsc, node_t * local_node, enum rsc_role_e filter, gboolean current);
+bool assign_node(resource_t * rsc, node_t * node, gboolean force);
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"
# define LOAD_STOPPED "load_stopped"
#endif
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 8, 6:19 PM (12 h, 11 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002576
Default Alt Text
(80 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment