Page Menu
Home
ClusterLabs Projects
Search
Configure Global Search
Log In
Files
F4624070
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
196 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/pengine/utils.c b/lib/pengine/utils.c
index 34fe970a95..741044c80b 100644
--- a/lib/pengine/utils.c
+++ b/lib/pengine/utils.c
@@ -1,1344 +1,1367 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser 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
*/
#include <crm_internal.h>
#include <crm/crm.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/util.h>
#include <glib.h>
#include <crm/pengine/rules.h>
#include <utils.h>
pe_working_set_t *pe_dataset = NULL;
extern xmlNode *get_object_root(const char *object_type,xmlNode *the_root);
void print_str_str(gpointer key, gpointer value, gpointer user_data);
gboolean ghash_free_str_str(gpointer key, gpointer value, gpointer user_data);
void unpack_operation(
action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set);
node_t *
node_copy(node_t *this_node)
{
node_t *new_node = NULL;
CRM_CHECK(this_node != NULL, return NULL);
crm_malloc0(new_node, sizeof(node_t));
CRM_ASSERT(new_node != NULL);
crm_debug_5("Copying %p (%s) to %p",
this_node, this_node->details->uname, new_node);
new_node->weight = this_node->weight;
new_node->fixed = this_node->fixed;
new_node->details = this_node->details;
return new_node;
}
/* any node in list1 or list2 and not in the other gets a score of -INFINITY */
void
node_list_exclude(GHashTable *hash, GListPtr list, gboolean merge_scores)
{
GHashTable *result = hash;
node_t *other_node = NULL;
GListPtr gIter = list;
GHashTableIter iter;
node_t *node = NULL;
g_hash_table_iter_init (&iter, hash);
while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) {
other_node = pe_find_node_id(list, node->details->id);
if(other_node == NULL) {
node->weight = -INFINITY;
} else if(merge_scores) {
node->weight = merge_weights(node->weight, other_node->weight);
}
}
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
other_node = pe_hash_table_lookup(result, node->details->id);
if(other_node == NULL) {
node_t *new_node = node_copy(node);
new_node->weight = -INFINITY;
g_hash_table_insert(result, (gpointer)new_node->details->id, new_node);
}
}
}
GHashTable *
node_hash_from_list(GListPtr list)
{
GListPtr gIter = list;
GHashTable *result = g_hash_table_new_full(
crm_str_hash,g_str_equal, NULL, g_hash_destroy_str);
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
node_t *n = node_copy(node);
g_hash_table_insert(result, (gpointer)n->details->id, n);
}
return result;
}
GListPtr
node_list_dup(GListPtr list1, gboolean reset, gboolean filter)
{
GListPtr result = NULL;
GListPtr gIter = list1;
for(; gIter != NULL; gIter = gIter->next) {
node_t *new_node = NULL;
node_t *this_node = (node_t*)gIter->data;
if(filter && this_node->weight < 0) {
continue;
}
new_node = node_copy(this_node);
if(reset) {
new_node->weight = 0;
}
if(new_node != NULL) {
result = g_list_prepend(result, new_node);
}
}
return result;
}
static gint
sort_node_uname(gconstpointer a, gconstpointer b)
{
const node_t *node_a = a;
const node_t *node_b = b;
return strcmp(node_a->details->uname, node_b->details->uname);
}
void dump_node_scores_worker(int level, const char *file, const char *function, int line,
resource_t *rsc, const char *comment, GHashTable *nodes)
{
GHashTable *hash = nodes;
GHashTableIter iter;
node_t *node = NULL;
if(rsc) {
hash = rsc->allowed_nodes;
}
if(rsc && is_set(rsc->flags, pe_rsc_orphan)) {
/* Don't show the allocation scores for orphans */
return;
}
if(level == 0) {
/* For now we want this in sorted order to keep the regression tests happy */
GListPtr gIter = NULL;
GListPtr list = g_hash_table_get_values(hash);
list = g_list_sort(list, sort_node_uname);
gIter = list;
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
char *score = score2char(node->weight);
if(rsc) {
printf("%s: %s allocation score on %s: %s\n",
comment, rsc->id, node->details->uname, score);
} else {
printf("%s: %s = %s\n", comment, node->details->uname, score);
}
crm_free(score);
}
g_list_free(list);
} else if(hash) {
g_hash_table_iter_init (&iter, hash);
while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) {
char *score = score2char(node->weight);
if(rsc) {
do_crm_log_alias(level, file, function, line, "%s: %s allocation score on %s: %s",
comment, rsc->id, node->details->uname, score);
} else {
do_crm_log_alias(level, file, function, line, "%s: %s = %s", comment, node->details->uname, score);
}
crm_free(score);
}
}
if(rsc && rsc->children) {
GListPtr gIter = NULL;
gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
dump_node_scores_worker(level, file, function, line, child, comment, nodes);
}
}
}
static void
append_dump_text(gpointer key, gpointer value, gpointer user_data)
{
char **dump_text = user_data;
int len = 0;
char *new_text = NULL;
len = strlen(*dump_text) + strlen(" ") + strlen(key) + strlen("=") + strlen(value) + 1;
crm_malloc0(new_text, len);
sprintf(new_text, "%s %s=%s", *dump_text, (char *)key, (char *)value);
crm_free(*dump_text);
*dump_text = new_text;
}
void
dump_node_capacity(int level, const char *comment, node_t *node)
{
int len = 0;
char *dump_text = NULL;
len = strlen(comment) + strlen(": ") + strlen(node->details->uname) + strlen(" capacity:") + 1;
crm_malloc0(dump_text, len);
sprintf(dump_text, "%s: %s capacity:", comment, node->details->uname);
g_hash_table_foreach(node->details->utilization, append_dump_text, &dump_text);
if(level == 0) {
fprintf(stdout, "%s\n", dump_text);
} else {
do_crm_log_unlikely(level, "%s", dump_text);
}
crm_free(dump_text);
}
void
dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node)
{
int len = 0;
char *dump_text = NULL;
len = strlen(comment) + strlen(": ") + strlen(rsc->id) + strlen(" utilization on ")
+ strlen(node->details->uname) + strlen(":") + 1;
crm_malloc0(dump_text, len);
sprintf(dump_text, "%s: %s utilization on %s:", comment, rsc->id, node->details->uname);
g_hash_table_foreach(rsc->utilization, append_dump_text, &dump_text);
if(level == 0) {
fprintf(stdout, "%s\n", dump_text);
} else {
do_crm_log_unlikely(level, "%s", dump_text);
}
crm_free(dump_text);
}
gint sort_rsc_index(gconstpointer a, gconstpointer b)
{
const resource_t *resource1 = (const resource_t*)a;
const resource_t *resource2 = (const resource_t*)b;
if(a == NULL && b == NULL) { return 0; }
if(a == NULL) { return 1; }
if(b == NULL) { return -1; }
if(resource1->sort_index > resource2->sort_index) {
return -1;
}
if(resource1->sort_index < resource2->sort_index) {
return 1;
}
return 0;
}
gint sort_rsc_priority(gconstpointer a, gconstpointer b)
{
const resource_t *resource1 = (const resource_t*)a;
const resource_t *resource2 = (const resource_t*)b;
if(a == NULL && b == NULL) { return 0; }
if(a == NULL) { return 1; }
if(b == NULL) { return -1; }
if(resource1->priority > resource2->priority) {
return -1;
}
if(resource1->priority < resource2->priority) {
return 1;
}
return 0;
}
action_t *
custom_action(resource_t *rsc, char *key, const char *task,
node_t *on_node, gboolean optional, gboolean save_action,
pe_working_set_t *data_set)
{
action_t *action = NULL;
GListPtr possible_matches = NULL;
CRM_CHECK(key != NULL, return NULL);
CRM_CHECK(task != NULL, return NULL);
if(save_action && rsc != NULL) {
possible_matches = find_actions(rsc->actions, key, on_node);
}
if(possible_matches != NULL) {
crm_free(key);
if(g_list_length(possible_matches) > 1) {
pe_warn("Action %s for %s on %s exists %d times",
task, rsc?rsc->id:"<NULL>",
on_node?on_node->details->uname:"<NULL>",
g_list_length(possible_matches));
}
action = g_list_nth_data(possible_matches, 0);
crm_debug_4("Found existing action (%d) %s for %s on %s",
action->id, task, rsc?rsc->id:"<NULL>",
on_node?on_node->details->uname:"<NULL>");
g_list_free(possible_matches);
}
if(action == NULL) {
if(save_action) {
crm_debug_4("Creating%s action %d: %s for %s on %s",
optional?"":" manditory", data_set->action_id, key, rsc?rsc->id:"<NULL>",
on_node?on_node->details->uname:"<NULL>");
}
crm_malloc0(action, sizeof(action_t));
if(save_action) {
action->id = data_set->action_id++;
} else {
action->id = 0;
}
action->rsc = rsc;
CRM_ASSERT(task != NULL);
action->task = crm_strdup(task);
if(on_node) {
action->node = node_copy(on_node);
}
action->uuid = key;
set_bit_inplace(action->flags, pe_action_failure_is_fatal);
set_bit_inplace(action->flags, pe_action_runnable);
if(optional) {
set_bit_inplace(action->flags, pe_action_optional);
} else {
clear_bit_inplace(action->flags, pe_action_optional);
}
/*
Implied by crm_malloc0()...
action->actions_before = NULL;
action->actions_after = NULL;
action->pseudo = FALSE;
action->dumped = FALSE;
action->processed = FALSE;
action->seen_count = 0;
*/
action->extra = g_hash_table_new_full(
crm_str_hash, g_str_equal, free, free);
action->meta = g_hash_table_new_full(
crm_str_hash, g_str_equal, free, free);
if(save_action) {
data_set->actions = g_list_prepend(
data_set->actions, action);
}
if(rsc != NULL) {
action->op_entry = find_rsc_op_entry(rsc, key);
unpack_operation(
action, action->op_entry, data_set);
if(save_action) {
rsc->actions = g_list_prepend(
rsc->actions, action);
}
}
if(save_action) {
crm_debug_4("Action %d created", action->id);
}
}
if(optional == FALSE && (action->flags & pe_action_optional)) {
crm_debug_2("Action %d (%s) marked manditory",
action->id, action->uuid);
clear_bit_inplace(action->flags, pe_action_optional);
}
if(rsc != NULL) {
enum action_tasks a_task = text2task(action->task);
int warn_level = LOG_DEBUG_3;
if(save_action) {
warn_level = LOG_WARNING;
}
if(is_set(action->flags, pe_action_have_node_attrs) == FALSE
&& action->node != NULL
&& action->op_entry != NULL) {
set_bit_inplace(action->flags, pe_action_have_node_attrs);
unpack_instance_attributes(
data_set->input, action->op_entry, XML_TAG_ATTR_SETS,
action->node->details->attrs,
action->extra, NULL, FALSE, data_set->now);
}
if(is_set(action->flags, pe_action_pseudo)) {
/* leave untouched */
} else if(action->node == NULL) {
clear_bit_inplace(action->flags, pe_action_runnable);
} else if(is_not_set(rsc->flags, pe_rsc_managed)
&& g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL) == NULL) {
do_crm_log_unlikely(LOG_DEBUG, "Action %s (unmanaged)",
action->uuid);
set_bit_inplace(action->flags, pe_action_optional);
/* action->runnable = FALSE; */
} else if(action->node->details->online == FALSE) {
clear_bit_inplace(action->flags, pe_action_runnable);
do_crm_log(warn_level, "Action %s on %s is unrunnable (offline)",
action->uuid, action->node->details->uname);
if(is_set(action->rsc->flags, pe_rsc_managed)
&& save_action
&& a_task == stop_rsc) {
do_crm_log(warn_level, "Marking node %s unclean",
action->node->details->uname);
action->node->details->unclean = TRUE;
}
} else if(action->node->details->pending) {
clear_bit_inplace(action->flags, pe_action_runnable);
do_crm_log(warn_level, "Action %s on %s is unrunnable (pending)",
action->uuid, action->node->details->uname);
} else if(action->needs == rsc_req_nothing) {
crm_debug_3("Action %s doesnt require anything",
action->uuid);
set_bit_inplace(action->flags, pe_action_runnable);
#if 0
/*
* No point checking this
* - if we dont have quorum we cant stonith anyway
*/
} else if(action->needs == rsc_req_stonith) {
crm_debug_3("Action %s requires only stonith", action->uuid);
action->runnable = TRUE;
#endif
} else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE
&& data_set->no_quorum_policy == no_quorum_stop) {
clear_bit_inplace(action->flags, pe_action_runnable);
crm_debug("%s\t%s (cancelled : quorum)",
action->node->details->uname,
action->uuid);
} else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE
&& data_set->no_quorum_policy == no_quorum_freeze) {
crm_debug_3("Check resource is already active");
if(rsc->fns->active(rsc, TRUE) == FALSE) {
clear_bit_inplace(action->flags, pe_action_runnable);
crm_debug("%s\t%s (cancelled : quorum freeze)",
action->node->details->uname,
action->uuid);
}
} else {
crm_debug_3("Action %s is runnable", action->uuid);
set_bit_inplace(action->flags, pe_action_runnable);
}
if(save_action) {
switch(a_task) {
case stop_rsc:
set_bit(rsc->flags, pe_rsc_stopping);
break;
case start_rsc:
clear_bit(rsc->flags, pe_rsc_starting);
if(is_set(action->flags, pe_action_runnable)) {
set_bit(rsc->flags, pe_rsc_starting);
}
break;
default:
break;
}
}
}
return action;
}
void
unpack_operation(
action_t *action, xmlNode *xml_obj, pe_working_set_t* data_set)
{
int value_i = 0;
unsigned long long interval = 0;
unsigned long long start_delay = 0;
char *value_ms = NULL;
const char *class = NULL;
const char *value = NULL;
const char *field = NULL;
CRM_CHECK(action->rsc != NULL, return);
unpack_instance_attributes(data_set->input, data_set->op_defaults, XML_TAG_META_SETS, NULL,
action->meta, NULL, FALSE, data_set->now);
xml_prop_iter(xml_obj, name, value,
if (value != NULL) {
g_hash_table_replace(action->meta, crm_strdup(name), crm_strdup(value));
}
);
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_META_SETS,
NULL, action->meta, NULL, FALSE, data_set->now);
unpack_instance_attributes(data_set->input, xml_obj, XML_TAG_ATTR_SETS,
NULL, action->meta, NULL, FALSE, data_set->now);
g_hash_table_remove(action->meta, "id");
class = g_hash_table_lookup(action->rsc->meta, "class");
value = g_hash_table_lookup(action->meta, "requires");
if(safe_str_eq(class, "stonith")) {
action->needs = rsc_req_nothing;
value = "nothing (fencing op)";
} else if(safe_str_eq(value, "nothing")) {
action->needs = rsc_req_nothing;
} else if(safe_str_eq(value, "quorum")) {
action->needs = rsc_req_quorum;
} else if(is_set(data_set->flags, pe_flag_stonith_enabled)
&& safe_str_eq(value, "fencing")) {
action->needs = rsc_req_stonith;
} else {
if(value) {
crm_config_err("Invalid value for %s->requires: %s%s",
action->rsc->id, value,
is_set(data_set->flags, pe_flag_stonith_enabled)?"":" (stonith-enabled=false)");
}
if (safe_str_eq(action->task, CRMD_ACTION_STATUS)
|| safe_str_eq(action->task, CRMD_ACTION_NOTIFY)) {
action->needs = rsc_req_nothing;
value = "nothing (default)";
} else if (data_set->no_quorum_policy == no_quorum_stop
&& safe_str_neq(action->task, CRMD_ACTION_START)) {
action->needs = rsc_req_nothing;
value = "nothing (default)";
} else if (is_set(data_set->flags, pe_flag_stonith_enabled)) {
action->needs = rsc_req_stonith;
value = "fencing (default)";
} else {
action->needs = rsc_req_quorum;
value = "quorum (default)";
}
}
crm_debug_3("\tAction %s requires: %s", action->task, value);
value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ON_FAIL);
if(safe_str_eq(action->task, CRMD_ACTION_STOP)
&& safe_str_eq(value, "standby")) {
crm_config_err("on-fail=standby is not allowed for stop actions: %s", action->rsc->id);
value = NULL;
}
if(value == NULL) {
} else if(safe_str_eq(value, "block")) {
action->on_fail = action_fail_block;
} else if(safe_str_eq(value, "fence")) {
action->on_fail = action_fail_fence;
value = "node fencing";
if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
crm_config_err("Specifying on_fail=fence and"
" stonith-enabled=false makes no sense");
action->on_fail = action_fail_stop;
action->fail_role = RSC_ROLE_STOPPED;
value = "stop resource";
}
} else if(safe_str_eq(value, "standby")) {
action->on_fail = action_fail_standby;
value = "node standby";
} else if(safe_str_eq(value, "ignore")
|| safe_str_eq(value, "nothing")) {
action->on_fail = action_fail_ignore;
value = "ignore";
} else if(safe_str_eq(value, "migrate")) {
action->on_fail = action_fail_migrate;
value = "force migration";
} else if(safe_str_eq(value, "stop")) {
action->on_fail = action_fail_stop;
action->fail_role = RSC_ROLE_STOPPED;
value = "stop resource";
} else if(safe_str_eq(value, "restart")) {
action->on_fail = action_fail_recover;
value = "restart (and possibly migrate)";
} else {
pe_err("Resource %s: Unknown failure type (%s)",
action->rsc->id, value);
value = NULL;
}
/* defaults */
if(value == NULL && safe_str_eq(action->task, CRMD_ACTION_STOP)) {
if(is_set(data_set->flags, pe_flag_stonith_enabled)) {
action->on_fail = action_fail_fence;
value = "resource fence (default)";
} else {
action->on_fail = action_fail_block;
value = "resource block (default)";
}
} else if(value == NULL) {
action->on_fail = action_fail_recover;
value = "restart (and possibly migrate) (default)";
}
crm_debug_3("\t%s failure handling: %s", action->task, value);
value = NULL;
if(xml_obj != NULL) {
value = g_hash_table_lookup(action->meta, "role_after_failure");
}
if(value != NULL && action->fail_role == RSC_ROLE_UNKNOWN) {
action->fail_role = text2role(value);
}
/* defaults */
if(action->fail_role == RSC_ROLE_UNKNOWN) {
if(safe_str_eq(action->task, CRMD_ACTION_PROMOTE)) {
action->fail_role = RSC_ROLE_SLAVE;
} else {
action->fail_role = RSC_ROLE_STARTED;
}
}
crm_debug_3("\t%s failure results in: %s",
action->task, role2text(action->fail_role));
field = XML_LRM_ATTR_INTERVAL;
value = g_hash_table_lookup(action->meta, field);
if(value != NULL) {
interval = crm_get_interval(value);
if(interval > 0) {
value_ms = crm_itoa(interval);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
} else {
g_hash_table_remove(action->meta, field);
}
}
field = XML_OP_ATTR_START_DELAY;
value = g_hash_table_lookup(action->meta, field);
if(value != NULL) {
value_i = crm_get_msec(value);
if(value_i < 0) {
value_i = 0;
}
start_delay = value_i;
value_ms = crm_itoa(value_i);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
} else if(interval > 0 && g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN)) {
char *date_str = NULL;
char *date_str_mutable = NULL;
ha_time_t *origin = NULL;
value = g_hash_table_lookup(action->meta, XML_OP_ATTR_ORIGIN);
date_str = crm_strdup(value);
date_str_mutable = date_str;
origin = parse_date(&date_str_mutable);
crm_free(date_str);
if(origin == NULL) {
crm_config_err("Operation %s contained an invalid "XML_OP_ATTR_ORIGIN": %s",
ID(xml_obj), value);
} else {
ha_time_t *delay = NULL;
int rc = compare_date(origin, data_set->now);
unsigned long long delay_s = 0;
while(rc < 0) {
add_seconds(origin, interval/1000);
rc = compare_date(origin, data_set->now);
}
delay = subtract_time(origin, data_set->now);
delay_s = date_in_seconds(delay);
/* log_date(LOG_DEBUG_5, "delay", delay, ha_log_date|ha_log_time|ha_log_local); */
crm_info("Calculated a start delay of %llus for %s", delay_s, ID(xml_obj));
g_hash_table_replace(action->meta, crm_strdup(XML_OP_ATTR_START_DELAY), crm_itoa(delay_s * 1000));
start_delay = delay_s * 1000;
free_ha_date(origin);
free_ha_date(delay);
}
}
field = XML_ATTR_TIMEOUT;
value = g_hash_table_lookup(action->meta, field);
if(value == NULL) {
value = pe_pref(
data_set->config_hash, "default-action-timeout");
}
value_i = crm_get_msec(value);
if(value_i < 0) {
value_i = 0;
}
value_i += start_delay;
value_ms = crm_itoa(value_i);
g_hash_table_replace(action->meta, crm_strdup(field), value_ms);
}
xmlNode *
find_rsc_op_entry(resource_t *rsc, const char *key)
{
int number = 0;
gboolean do_retry = TRUE;
char *local_key = NULL;
const char *name = NULL;
const char *value = NULL;
const char *interval = NULL;
char *match_key = NULL;
xmlNode *op = NULL;
xmlNode *operation = NULL;
retry:
for(operation = __xml_first_child(rsc->ops_xml); operation != NULL; operation = __xml_next(operation)) {
if(crm_str_eq((const char *)operation->name, "op", TRUE)) {
name = crm_element_value(operation, "name");
interval = crm_element_value(operation, XML_LRM_ATTR_INTERVAL);
value = crm_element_value(operation, "enabled");
if(value && crm_is_true(value) == FALSE) {
continue;
}
number = crm_get_interval(interval);
if(number < 0) {
continue;
}
match_key = generate_op_key(rsc->id, name, number);
if(safe_str_eq(key, match_key)) {
op = operation;
}
crm_free(match_key);
if(op != NULL) {
crm_free(local_key);
return op;
}
}
}
crm_free(local_key);
if(do_retry == FALSE) {
return NULL;
}
do_retry = FALSE;
if(strstr(key, CRMD_ACTION_MIGRATE) || strstr(key, CRMD_ACTION_MIGRATED)) {
local_key = generate_op_key(rsc->id, "migrate", 0);
key = local_key;
goto retry;
} else if(strstr(key, "_notify_")) {
local_key = generate_op_key(rsc->id, "notify", 0);
key = local_key;
goto retry;
}
return NULL;
}
void
print_node(const char *pre_text, node_t *node, gboolean details)
{
if(node == NULL) {
crm_debug_4("%s%s: <NULL>",
pre_text==NULL?"":pre_text,
pre_text==NULL?"":": ");
return;
}
crm_debug_4("%s%s%sNode %s: (weight=%d, fixed=%s)",
pre_text==NULL?"":pre_text,
pre_text==NULL?"":": ",
node->details==NULL?"error ":node->details->online?"":"Unavailable/Unclean ",
node->details->uname,
node->weight,
node->fixed?"True":"False");
if(details && node != NULL && node->details != NULL) {
char *pe_mutable = crm_strdup("\t\t");
GListPtr gIter = node->details->running_rsc;
crm_debug_4("\t\t===Node Attributes");
g_hash_table_foreach(node->details->attrs,
print_str_str, pe_mutable);
crm_free(pe_mutable);
crm_debug_4("\t\t=== Resources");
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
print_resource(LOG_DEBUG_4, "\t\t", rsc, FALSE);
}
}
}
/*
* Used by the HashTable for-loop
*/
void print_str_str(gpointer key, gpointer value, gpointer user_data)
{
crm_debug_4("%s%s %s ==> %s",
user_data==NULL?"":(char*)user_data,
user_data==NULL?"":": ",
(char*)key,
(char*)value);
}
void
print_resource(
int log_level, const char *pre_text, resource_t *rsc, gboolean details)
{
long options = pe_print_log;
if(rsc == NULL) {
do_crm_log(log_level-1, "%s%s: <NULL>",
pre_text==NULL?"":pre_text,
pre_text==NULL?"":": ");
return;
}
if(details) {
options |= pe_print_details;
}
rsc->fns->print(rsc, pre_text, options, &log_level);
}
void
pe_free_action(action_t *action)
{
if(action == NULL) {
return;
}
slist_basic_destroy(action->actions_before);/* action_warpper_t* */
slist_basic_destroy(action->actions_after); /* action_warpper_t* */
if(action->extra) {
g_hash_table_destroy(action->extra);
}
if(action->meta) {
g_hash_table_destroy(action->meta);
}
crm_free(action->task);
crm_free(action->uuid);
crm_free(action->node);
crm_free(action);
}
GListPtr
find_recurring_actions(GListPtr input, node_t *not_on_node)
{
const char *value = NULL;
GListPtr result = NULL;
GListPtr gIter = input;
CRM_CHECK(input != NULL, return NULL);
for(; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t*)gIter->data;
value = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
if(value == NULL) {
/* skip */
} else if(safe_str_eq(value, "0")) {
/* skip */
} else if(safe_str_eq(CRMD_ACTION_CANCEL, action->task)) {
/* skip */
} else if(not_on_node == NULL) {
crm_debug_5("(null) Found: %s", action->uuid);
result = g_list_prepend(result, action);
} else if(action->node == NULL) {
/* skip */
} else if(action->node->details != not_on_node->details) {
crm_debug_5("Found: %s", action->uuid);
result = g_list_prepend(result, action);
}
}
return result;
}
+enum action_tasks
+get_complex_task(resource_t *rsc, const char *name, gboolean allow_non_atomic)
+{
+ enum action_tasks task = text2task(name);
+ if(rsc == NULL) {
+ return task;
+
+ } else if(allow_non_atomic == FALSE || rsc->variant == pe_native) {
+ switch(task) {
+ case stopped_rsc:
+ case started_rsc:
+ case action_demoted:
+ case action_promoted:
+ crm_trace("Folding %s back into its atomic counterpart for %s", name, rsc->id);
+ return task-1;
+ break;
+ default:
+ break;
+ }
+ }
+ return task;
+}
+
action_t *
find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node)
{
- GListPtr gIter = input;
+ GListPtr gIter = NULL;
CRM_CHECK(uuid || task, return NULL);
- for(; gIter != NULL; gIter = gIter->next) {
+ for(gIter = input; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t*)gIter->data;
if(uuid != NULL && safe_str_neq(uuid, action->uuid)) {
continue;
} else if(task != NULL && safe_str_neq(task, action->task)) {
continue;
} else if(on_node == NULL) {
return action;
} else if(action->node == NULL) {
continue;
} else if(on_node->details == action->node->details) {
return action;
}
}
return NULL;
}
GListPtr
find_actions(GListPtr input, const char *key, node_t *on_node)
{
GListPtr gIter = input;
GListPtr result = NULL;
CRM_CHECK(key != NULL, return NULL);
for(; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t*)gIter->data;
crm_debug_5("Matching %s against %s", key, action->uuid);
if(safe_str_neq(key, action->uuid)) {
continue;
} else if(on_node == NULL) {
result = g_list_prepend(result, action);
} else if(action->node == NULL) {
/* skip */
crm_debug_2("While looking for %s action on %s, "
"found an unallocated one. Assigning"
" it to the requested node...",
key, on_node->details->uname);
action->node = node_copy(on_node);
result = g_list_prepend(result, action);
} else if(on_node->details == action->node->details) {
result = g_list_prepend(result, action);
}
}
return result;
}
GListPtr
find_actions_exact(GListPtr input, const char *key, node_t *on_node)
{
GListPtr gIter = input;
GListPtr result = NULL;
CRM_CHECK(key != NULL, return NULL);
for(; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t*)gIter->data;
crm_debug_5("Matching %s against %s", key, action->uuid);
if(safe_str_neq(key, action->uuid)) {
crm_debug_3("Key mismatch: %s vs. %s",
key, action->uuid);
continue;
} else if(on_node == NULL || action->node == NULL) {
crm_debug_3("on_node=%p, action->node=%p",
on_node, action->node);
continue;
} else if(safe_str_eq(on_node->details->id,
action->node->details->id)) {
result = g_list_prepend(result, action);
}
crm_debug_2("Node mismatch: %s vs. %s",
on_node->details->id, action->node->details->id);
}
return result;
}
static void
resource_node_score(resource_t *rsc, node_t *node, int score, const char *tag)
{
node_t *match = NULL;
if(rsc->children) {
GListPtr gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
resource_node_score(child_rsc, node, score, tag);
}
}
crm_debug_2("Setting %s for %s on %s: %d",
tag, rsc->id, node->details->uname, score);
match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
if(match == NULL) {
match = node_copy(node);
match->weight = merge_weights(score, node->weight);
g_hash_table_insert(rsc->allowed_nodes, (gpointer)match->details->id, match);
}
match->weight = merge_weights(match->weight, score);
}
void
resource_location(resource_t *rsc, node_t *node, int score, const char *tag,
pe_working_set_t *data_set)
{
if(node != NULL) {
resource_node_score(rsc, node, score, tag);
} else if(data_set != NULL) {
GListPtr gIter = data_set->nodes;
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
resource_node_score(rsc, node, score, tag);
}
} else {
GHashTableIter iter;
node_t *node = NULL;
g_hash_table_iter_init (&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) {
resource_node_score(rsc, node, score, tag);
}
}
if(node == NULL && score == -INFINITY) {
if(rsc->allocated_to) {
crm_info("Deallocating %s from %s", rsc->id, rsc->allocated_to->details->uname);
crm_free(rsc->allocated_to);
rsc->allocated_to = NULL;
}
}
}
#define sort_return(an_int, why) do { \
crm_free(a_uuid); \
crm_free(b_uuid); \
crm_trace("%s (%d) %c %s (%d) : %s", \
a_xml_id, a_call_id, an_int>0?'>':an_int<0?'<':'=', \
b_xml_id, b_call_id, why); \
return an_int; \
} while(0)
gint
sort_op_by_callid(gconstpointer a, gconstpointer b)
{
char *a_uuid = NULL;
char *b_uuid = NULL;
const xmlNode *xml_a = a;
const xmlNode *xml_b = b;
const char *a_xml_id = crm_element_value_const(xml_a, XML_ATTR_ID);
const char *b_xml_id = crm_element_value_const(xml_b, XML_ATTR_ID);
const char *a_task_id = crm_element_value_const(xml_a, XML_LRM_ATTR_CALLID);
const char *b_task_id = crm_element_value_const(xml_b, XML_LRM_ATTR_CALLID);
const char *a_key = crm_element_value_const(xml_a, XML_ATTR_TRANSITION_MAGIC);
const char *b_key = crm_element_value_const(xml_b, XML_ATTR_TRANSITION_MAGIC);
int dummy = -1;
int a_id = -1;
int b_id = -1;
int a_rc = -1;
int b_rc = -1;
int a_status = -1;
int b_status = -1;
int a_call_id = -1;
int b_call_id = -1;
if(safe_str_eq(a_xml_id, b_xml_id)) {
/* We have duplicate lrm_rsc_op entries in the status
* section which is unliklely to be a good thing
* - we can handle it easily enough, but we need to get
* to the bottom of why its happening.
*/
pe_err("Duplicate lrm_rsc_op entries named %s", a_xml_id);
sort_return(0, "duplicate");
}
CRM_CHECK(a_task_id != NULL, sort_return(0, "bad id a"));
CRM_CHECK(a_task_id != NULL, sort_return(0, "bad id b"));
a_call_id = crm_parse_int(a_task_id, NULL);
b_call_id = crm_parse_int(b_task_id, NULL);
if(a_call_id == -1 && b_call_id == -1) {
/* both are pending ops so it doesnt matter since
* stops are never pending
*/
sort_return(0, "pending");
} else if(a_call_id >= 0 && a_call_id < b_call_id) {
sort_return(-1, "call id");
} else if(b_call_id >= 0 && a_call_id > b_call_id) {
sort_return(1, "call id");
} else if(b_call_id >= 0 && a_call_id == b_call_id) {
/* The last op and last_failed_op are the same */
sort_return(0, "duplicate failure");
}
/* now process pending ops */
CRM_CHECK(a_key != NULL && b_key != NULL, sort_return(0, "No key"));
CRM_CHECK(decode_transition_magic(
a_key, &a_uuid, &a_id, &dummy, &a_status, &a_rc, &dummy),
sort_return(0, "bad magic a"));
CRM_CHECK(decode_transition_magic(
b_key, &b_uuid, &b_id, &dummy, &b_status, &b_rc, &dummy),
sort_return(0, "bad magic b"));
/* try and determin the relative age of the operation...
* some pending operations (ie. a start) may have been supuerceeded
* by a subsequent stop
*
* [a|b]_id == -1 means its a shutdown operation and _always_ comes last
*/
if(safe_str_neq(a_uuid, b_uuid) || a_id == b_id) {
/*
* some of the logic in here may be redundant...
*
* if the UUID from the TE doesnt match then one better
* be a pending operation.
* pending operations dont survive between elections and joins
* because we query the LRM directly
*/
CRM_CHECK(a_call_id == -1 || b_call_id == -1, sort_return(0, "odd"));
CRM_CHECK(a_call_id >= 0 || b_call_id >= 0, sort_return(0, "odd"));
if(b_call_id == -1) {
sort_return(-1, "transition + call");
} else if(a_call_id == -1) {
sort_return(1, "transition + call");
}
} else if((a_id >= 0 && a_id < b_id) || b_id == -1) {
sort_return(-1, "transition");
} else if((b_id >= 0 && a_id > b_id) || a_id == -1) {
sort_return(1, "transition");
}
/* we should never end up here */
CRM_CHECK(FALSE, sort_return(0, "default"));
}
time_t get_timet_now(pe_working_set_t *data_set)
{
time_t now = 0;
if(data_set && data_set->now) {
now = data_set->now->tm_now;
}
if(now == 0) {
/* eventually we should convert data_set->now into time_tm
* for now, its only triggered by PE regression tests
*/
now = time(NULL);
crm_crit("Defaulting to 'now'");
if(data_set && data_set->now) {
data_set->now->tm_now = now;
}
}
return now;
}
struct fail_search
{
resource_t *rsc;
int count;
long long last;
char *key;
};
static void get_failcount_by_prefix(gpointer key_p, gpointer value, gpointer user_data)
{
struct fail_search *search = user_data;
const char *key = key_p;
const char *match = strstr(key, search->key);
if(match) {
if(strstr(key, "last-failure-") == key && (key+13) == match) {
search->last = crm_int_helper(value, NULL);
} else if(strstr(key, "fail-count-") == key && (key+11) == match) {
search->count += char2score(value);
}
}
}
int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set)
{
struct fail_search search = {rsc, 0, 0, NULL};
search.key = crm_strdup(rsc->id);
if(is_not_set(rsc->flags, pe_rsc_unique)) {
int lpc = 0;
search.rsc = uber_parent(rsc);
/* Strip the clone incarnation */
for(lpc = strlen(search.key); lpc > 0; lpc--) {
if(search.key[lpc] == ':') {
search.key[lpc+1] = 0;
break;
}
}
g_hash_table_foreach(node->details->attrs, get_failcount_by_prefix, &search);
} else {
/* Optimize the "normal" case */
char *key = NULL;
const char *value = NULL;
key = crm_concat("fail-count", rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.count = char2score(value);
crm_free(key);
key = crm_concat("last-failure", rsc->id, '-');
value = g_hash_table_lookup(node->details->attrs, key);
search.last = crm_int_helper(value, NULL);
crm_free(key);
}
if(search.count != 0 && search.last != 0 && rsc->failure_timeout) {
if(last_failure) {
*last_failure = search.last;
}
if(search.last > 0) {
time_t now = get_timet_now(data_set);
if(now > (search.last + rsc->failure_timeout)) {
crm_notice("Failcount for %s on %s has expired (limit was %ds)",
search.rsc->id, node->details->uname, rsc->failure_timeout);
search.count = 0;
}
}
}
if(search.count != 0) {
crm_info("%s has failed %s times on %s",
search.rsc->id, score2char(search.count), node->details->uname);
}
crm_free(search.key);
return search.count;
}
gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role)
{
enum rsc_role_e local_role = RSC_ROLE_UNKNOWN;
const char *value = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_TARGET_ROLE);
CRM_CHECK(role != NULL, return FALSE);
if(value == NULL
|| safe_str_eq("started", value)
|| safe_str_eq("default", value)) {
return FALSE;
}
local_role = text2role(value);
if(local_role == RSC_ROLE_UNKNOWN) {
crm_config_err("%s: Unknown value for %s: %s",
rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
return FALSE;
} else if(local_role > RSC_ROLE_STARTED) {
if(uber_parent(rsc)->variant == pe_master) {
if(local_role > RSC_ROLE_SLAVE) {
/* This is what we'd do anyway, just leave the default to avoid messing up the placement algorithm */
return FALSE;
}
} else {
crm_config_err("%s is not part of a master/slave resource, a %s of '%s' makes no sense",
rsc->id, XML_RSC_ATTR_TARGET_ROLE, value);
return FALSE;
}
}
*role = local_role;
return TRUE;
}
diff --git a/lib/pengine/utils.h b/lib/pengine/utils.h
index d3a91162fe..5c2c3eaa6e 100644
--- a/lib/pengine/utils.h
+++ b/lib/pengine/utils.h
@@ -1,162 +1,163 @@
/*
* Copyright (C) 2004 Andrew Beekhof <andrew@beekhof.net>
*
* This library 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.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser 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 PE_UTILS__H
#define PE_UTILS__H
#include <crm/pengine/common.h>
#include <crm/pengine/status.h>
extern pe_working_set_t *pe_dataset;
extern node_t *node_copy(node_t *this_node) ;
extern time_t get_timet_now(pe_working_set_t *data_set);
extern int get_failcount(node_t *node, resource_t *rsc, int *last_failure, pe_working_set_t *data_set);
/* Binary like operators for lists of nodes */
extern void node_list_exclude(GHashTable *list, GListPtr list2, gboolean merge_scores);
extern GListPtr node_list_dup(GListPtr list, gboolean reset, gboolean filter);
extern GListPtr node_list_from_hash(GHashTable *hash, gboolean reset, gboolean filter);
extern GHashTable *node_hash_from_list(GListPtr list);
static inline gpointer pe_hash_table_lookup(GHashTable *hash, gconstpointer key)
{
if(hash) {
return g_hash_table_lookup(hash, key);
}
return NULL;
}
extern GListPtr node_list_and(GListPtr list1, GListPtr list2, gboolean filter);
extern GListPtr node_list_xor(GListPtr list1, GListPtr list2, gboolean filter);
extern GListPtr node_list_minus(GListPtr list1,GListPtr list2,gboolean filter);
extern void pe_free_shallow(GListPtr alist);
extern void pe_free_shallow_adv(GListPtr alist, gboolean with_data);
/* For creating the transition graph */
extern xmlNode *action2xml(action_t *action, gboolean as_input);
/* Printing functions for debug */
extern void print_node(
const char *pre_text, node_t *node, gboolean details);
extern void print_resource(
int log_level, const char *pre_text, resource_t *rsc, gboolean details);
extern void dump_node_scores_worker(int level, const char *file, const char *function, int line,
resource_t *rsc, const char *comment, GHashTable *nodes);
extern void dump_node_capacity(int level, const char *comment, node_t *node);
extern void dump_rsc_utilization(int level, const char *comment, resource_t *rsc, node_t *node);
#if SUPPORT_TRACING
# define dump_node_scores(level, rsc, text, nodes) do { \
static struct _pcmk_ddebug descriptor \
__attribute__((section("__verbose"), aligned(8))) = \
{ __func__, __FILE__, "node-scores", __LINE__, LOG_TRACE }; \
\
if((level) == 0 || __unlikely((level) < crm_log_level)) { \
dump_node_scores_worker(level, __FILE__, NULL, 0, rsc, text, nodes); \
\
} else if(__unlikely(descriptor.bump != LOG_TRACE)) { \
dump_node_scores_worker(LOG_NOTICE, __FILE__, __PRETTY_FUNCTION__, __LINE__, rsc, text, nodes); \
} \
} while(0)
#else
# define dump_node_scores(level, rsc, text, nodes) do { \
if((level) == 0 || __unlikely((level) < crm_log_level)) { \
dump_node_scores_worker(level, __FILE__, NULL, 0, rsc, text, nodes); \
} \
} while(0)
#endif
/* Sorting functions */
extern gint sort_rsc_priority(gconstpointer a, gconstpointer b);
extern gint sort_rsc_index(gconstpointer a, gconstpointer b);
extern xmlNode *find_rsc_op_entry(resource_t *rsc, const char *key);
extern action_t *custom_action(
resource_t *rsc, char *key, const char *task, node_t *on_node,
gboolean optional, gboolean foo, pe_working_set_t *data_set);
#define delete_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DELETE, 0)
#define delete_action(rsc, node, optional) custom_action( \
rsc, delete_key(rsc), CRMD_ACTION_DELETE, node, \
optional, TRUE, data_set);
#define stopped_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOPPED, 0)
#define stopped_action(rsc, node, optional) custom_action( \
rsc, stopped_key(rsc), CRMD_ACTION_STOPPED, node, \
optional, TRUE, data_set);
#define stop_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STOP, 0)
#define stop_action(rsc, node, optional) custom_action( \
rsc, stop_key(rsc), CRMD_ACTION_STOP, node, \
optional, TRUE, data_set);
#define start_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_START, 0)
#define start_action(rsc, node, optional) custom_action( \
rsc, start_key(rsc), CRMD_ACTION_START, node, \
optional, TRUE, data_set)
#define started_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_STARTED, 0)
#define started_action(rsc, node, optional) custom_action( \
rsc, started_key(rsc), CRMD_ACTION_STARTED, node, \
optional, TRUE, data_set)
#define promote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTE, 0)
#define promote_action(rsc, node, optional) custom_action( \
rsc, promote_key(rsc), CRMD_ACTION_PROMOTE, node, \
optional, TRUE, data_set)
#define promoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_PROMOTED, 0)
#define promoted_action(rsc, node, optional) custom_action( \
rsc, promoted_key(rsc), CRMD_ACTION_PROMOTED, node, \
optional, TRUE, data_set)
#define demote_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTE, 0)
#define demote_action(rsc, node, optional) custom_action( \
rsc, demote_key(rsc), CRMD_ACTION_DEMOTE, node, \
optional, TRUE, data_set)
#define demoted_key(rsc) generate_op_key(rsc->id, CRMD_ACTION_DEMOTED, 0)
#define demoted_action(rsc, node, optional) custom_action( \
rsc, demoted_key(rsc), CRMD_ACTION_DEMOTED, node, \
optional, TRUE, data_set)
extern action_t *find_first_action(GListPtr input, const char *uuid, const char *task, node_t *on_node);
+extern enum action_tasks get_complex_task(resource_t *rsc, const char *name, gboolean allow_non_atomic);
extern GListPtr find_actions(GListPtr input, const char *key, node_t *on_node);
extern GListPtr find_actions_exact(
GListPtr input, const char *key, node_t *on_node);
extern GListPtr find_recurring_actions(GListPtr input, node_t *not_on_node);
extern void pe_free_action(action_t *action);
extern void
resource_location(resource_t *rsc, node_t *node, int score, const char *tag,
pe_working_set_t *data_set);
extern gint sort_op_by_callid(gconstpointer a, gconstpointer b);
extern gboolean get_target_role(resource_t *rsc, enum rsc_role_e *role);
extern resource_t *find_clone_instance(resource_t *rsc, const char *sub_id, pe_working_set_t *data_set);
#endif
diff --git a/pengine/allocate.c b/pengine/allocate.c
index 025b1f1179..a3b76869d1 100644
--- a/pengine/allocate.c
+++ b/pengine/allocate.c
@@ -1,2155 +1,2156 @@
/*
* 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 <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <glib.h>
#include <crm/pengine/status.h>
#include <pengine.h>
#include <allocate.h>
#include <utils.h>
#include <lib/pengine/utils.h>
CRM_TRACE_INIT_DATA(pe_allocate);
void set_alloc_actions(pe_working_set_t *data_set);
void migrate_reload_madness(pe_working_set_t *data_set);
resource_alloc_functions_t resource_class_alloc_functions[] = {
{
native_merge_weights,
native_color,
native_create_actions,
native_create_probe,
native_internal_constraints,
native_rsc_colocation_lh,
native_rsc_colocation_rh,
native_rsc_location,
native_action_flags,
native_update_actions,
native_expand,
native_append_meta,
},
{
group_merge_weights,
group_color,
group_create_actions,
native_create_probe,
group_internal_constraints,
group_rsc_colocation_lh,
group_rsc_colocation_rh,
group_rsc_location,
group_action_flags,
group_update_actions,
group_expand,
group_append_meta,
},
{
native_merge_weights,
clone_color,
clone_create_actions,
clone_create_probe,
clone_internal_constraints,
clone_rsc_colocation_lh,
clone_rsc_colocation_rh,
clone_rsc_location,
clone_action_flags,
clone_update_actions,
clone_expand,
clone_append_meta,
},
{
native_merge_weights,
master_color,
master_create_actions,
clone_create_probe,
master_internal_constraints,
clone_rsc_colocation_lh,
master_rsc_colocation_rh,
clone_rsc_location,
clone_action_flags,
clone_update_actions,
clone_expand,
master_append_meta,
}
};
static gboolean
check_rsc_parameters(resource_t *rsc, node_t *node, xmlNode *rsc_entry,
pe_working_set_t *data_set)
{
int attr_lpc = 0;
gboolean force_restart = FALSE;
gboolean delete_resource = FALSE;
const char *value = NULL;
const char *old_value = NULL;
const char *attr_list[] = {
XML_ATTR_TYPE,
XML_AGENT_ATTR_CLASS,
XML_AGENT_ATTR_PROVIDER
};
for(; attr_lpc < DIMOF(attr_list); attr_lpc++) {
value = crm_element_value(rsc->xml, attr_list[attr_lpc]);
old_value = crm_element_value(rsc_entry, attr_list[attr_lpc]);
if(value == old_value /* ie. NULL */
|| crm_str_eq(value, old_value, TRUE)) {
continue;
}
force_restart = TRUE;
crm_notice("Forcing restart of %s on %s, %s changed: %s -> %s",
rsc->id, node->details->uname, attr_list[attr_lpc],
crm_str(old_value), crm_str(value));
}
if(force_restart) {
/* make sure the restart happens */
stop_action(rsc, node, FALSE);
set_bit(rsc->flags, pe_rsc_start_pending);
delete_resource = TRUE;
}
return delete_resource;
}
static void CancelXmlOp(resource_t *rsc, xmlNode *xml_op, node_t *active_node,
const char *reason, pe_working_set_t *data_set)
{
int interval = 0;
action_t *cancel = NULL;
char *key = NULL;
const char *task = NULL;
const char *call_id = NULL;
const char *interval_s = NULL;
CRM_CHECK(xml_op != NULL, return);
CRM_CHECK(active_node != NULL, return);
task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
call_id = crm_element_value(xml_op, XML_LRM_ATTR_CALLID);
interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
/* we need to reconstruct the key because of the way we used to construct resource IDs */
key = generate_op_key(rsc->id, task, interval);
crm_info("Action %s on %s will be stopped: %s",
key, active_node->details->uname, reason?reason:"unknown");
cancel = custom_action(rsc, crm_strdup(key), RSC_CANCEL,
active_node, FALSE, TRUE, data_set);
crm_free(cancel->task);
cancel->task = crm_strdup(RSC_CANCEL);
add_hash_param(cancel->meta, XML_LRM_ATTR_TASK, task);
add_hash_param(cancel->meta, XML_LRM_ATTR_CALLID, call_id);
add_hash_param(cancel->meta, XML_LRM_ATTR_INTERVAL, interval_s);
custom_action_order(rsc, stop_key(rsc), NULL,
rsc, NULL, cancel, pe_order_optional, data_set);
crm_free(key); key = NULL;
}
static gboolean
check_action_definition(resource_t *rsc, node_t *active_node, xmlNode *xml_op,
pe_working_set_t *data_set)
{
char *key = NULL;
int interval = 0;
const char *interval_s = NULL;
gboolean did_change = FALSE;
xmlNode *params_all = NULL;
xmlNode *params_restart = NULL;
GHashTable *local_rsc_params = NULL;
char *digest_all_calc = NULL;
const char *digest_all = NULL;
const char *restart_list = NULL;
const char *digest_restart = NULL;
char *digest_restart_calc = NULL;
action_t *action = NULL;
const char *task = crm_element_value(xml_op, XML_LRM_ATTR_TASK);
const char *op_version = crm_element_value(xml_op, XML_ATTR_CRM_VERSION);
CRM_CHECK(active_node != NULL, return FALSE);
if(safe_str_eq(task, RSC_STOP)) {
return FALSE;
}
interval_s = crm_element_value(xml_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
/* we need to reconstruct the key because of the way we used to construct resource IDs */
key = generate_op_key(rsc->id, task, interval);
if(interval > 0) {
xmlNode *op_match = NULL;
crm_debug_2("Checking parameters for %s", key);
op_match = find_rsc_op_entry(rsc, key);
if(op_match == NULL && is_set(data_set->flags, pe_flag_stop_action_orphans)) {
CancelXmlOp(rsc, xml_op, active_node, "orphan", data_set);
crm_free(key); key = NULL;
return TRUE;
} else if(op_match == NULL) {
crm_debug("Orphan action detected: %s on %s",
key, active_node->details->uname);
crm_free(key); key = NULL;
return TRUE;
}
}
action = custom_action(rsc, key, task, active_node, TRUE, FALSE, data_set);
local_rsc_params = g_hash_table_new_full(
crm_str_hash, g_str_equal,
g_hash_destroy_str, g_hash_destroy_str);
get_rsc_attributes(local_rsc_params, rsc, active_node, data_set);
params_all = create_xml_node(NULL, XML_TAG_PARAMS);
g_hash_table_foreach(local_rsc_params, hash2field, params_all);
g_hash_table_foreach(action->extra, hash2field, params_all);
g_hash_table_foreach(rsc->parameters, hash2field, params_all);
g_hash_table_foreach(action->meta, hash2metafield, params_all);
filter_action_parameters(params_all, op_version);
digest_all_calc = calculate_operation_digest(params_all, op_version);
digest_all = crm_element_value(xml_op, XML_LRM_ATTR_OP_DIGEST);
digest_restart = crm_element_value(xml_op, XML_LRM_ATTR_RESTART_DIGEST);
restart_list = crm_element_value(xml_op, XML_LRM_ATTR_OP_RESTART);
if(interval == 0 && safe_str_eq(task, RSC_STATUS)) {
/* Reload based on the start action not a probe */
task = RSC_START;
}
if(digest_restart) {
/* Changes that force a restart */
params_restart = copy_xml(params_all);
if(restart_list) {
filter_reload_parameters(params_restart, restart_list);
}
digest_restart_calc = calculate_operation_digest(params_restart, op_version);
if(safe_str_neq(digest_restart_calc, digest_restart)) {
did_change = TRUE;
crm_log_xml_info(params_restart, "params:restart");
crm_info("Parameters to %s on %s changed: recorded %s vs. %s (restart:%s) %s",
key, active_node->details->uname,
crm_str(digest_restart), digest_restart_calc,
op_version, crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
key = generate_op_key(rsc->id, task, interval);
custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
goto cleanup;
}
}
if(safe_str_neq(digest_all_calc, digest_all)) {
/* Changes that can potentially be handled by a reload */
action_t *op = NULL;
did_change = TRUE;
crm_log_xml_info(params_all, "params:reload");
crm_crit("Parameters to %s on %s changed: recorded %s vs. %s (reload:%s) %s",
key, active_node->details->uname,
crm_str(digest_all), digest_all_calc, op_version,
crm_element_value(xml_op, XML_ATTR_TRANSITION_MAGIC));
if(interval > 0) {
#if 0
/* Always reload/restart the entire resource */
op = custom_action(rsc, start_key(rsc), RSC_START, NULL, FALSE, TRUE, data_set);
update_action_flags(op, pe_action_allow_reload_conversion);
#else
/* Re-sending the recurring op is sufficient - the old one will be cancelled automatically */
key = generate_op_key(rsc->id, task, interval);
op = custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
custom_action_order(rsc, start_key(rsc), NULL,
NULL, NULL, op, pe_order_runnable_left, data_set);
#endif
} else if(digest_restart) {
crm_trace("Reloading '%s' action for resource %s", task, rsc->id);
/* Allow this resource to reload */
/* TODO: Set for the resource itself
* - thus avoiding causing depedant resources to restart
*/
key = generate_op_key(rsc->id, task, interval);
op = custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
update_action_flags(op, pe_action_allow_reload_conversion);
} else {
crm_trace("Resource %s doesn't know how to reload", rsc->id);
/* Re-send the start/demote/promote op
* Recurring ops will be detected independantly
*/
key = generate_op_key(rsc->id, task, interval);
custom_action(rsc, key, task, NULL, FALSE, TRUE, data_set);
}
}
cleanup:
free_xml(params_all);
free_xml(params_restart);
crm_free(digest_all_calc);
crm_free(digest_restart_calc);
g_hash_table_destroy(local_rsc_params);
pe_free_action(action);
return did_change;
}
extern gboolean DeleteRsc(resource_t *rsc, node_t *node, gboolean optional, pe_working_set_t *data_set);
static void
check_actions_for(xmlNode *rsc_entry, resource_t *rsc, node_t *node, pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
int offset = -1;
int interval = 0;
int stop_index = 0;
int start_index = 0;
const char *task = NULL;
const char *interval_s = NULL;
xmlNode *rsc_op = NULL;
GListPtr op_list = NULL;
GListPtr sorted_op_list = NULL;
gboolean is_probe = FALSE;
CRM_CHECK(node != NULL, return);
if(is_set(rsc->flags, pe_rsc_orphan)) {
crm_debug_2("Skipping param check for %s: orphan", rsc->id);
return;
} else if(pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
crm_debug_2("Skipping param check for %s: no longer active on %s",
rsc->id, node->details->uname);
return;
}
crm_debug_3("Processing %s on %s", rsc->id, node->details->uname);
if(check_rsc_parameters(rsc, node, rsc_entry, data_set)) {
DeleteRsc(rsc, node, FALSE, data_set);
}
for(rsc_op = __xml_first_child(rsc_entry); rsc_op != NULL; rsc_op = __xml_next(rsc_op)) {
if(crm_str_eq((const char *)rsc_op->name, XML_LRM_TAG_RSC_OP, TRUE)) {
op_list = g_list_prepend(op_list, rsc_op);
}
}
sorted_op_list = g_list_sort(op_list, sort_op_by_callid);
calculate_active_ops(sorted_op_list, &start_index, &stop_index);
for(gIter = sorted_op_list; gIter != NULL; gIter = gIter->next) {
xmlNode *rsc_op = (xmlNode*)gIter->data;
offset++;
if(start_index < stop_index) {
/* stopped */
continue;
} else if(offset < start_index) {
/* action occurred prior to a start */
continue;
}
is_probe = FALSE;
task = crm_element_value(rsc_op, XML_LRM_ATTR_TASK);
interval_s = crm_element_value(rsc_op, XML_LRM_ATTR_INTERVAL);
interval = crm_parse_int(interval_s, "0");
if(interval == 0 && safe_str_eq(task, RSC_STATUS)) {
is_probe = TRUE;
}
if(interval > 0 && is_set(data_set->flags, pe_flag_maintenance_mode)) {
CancelXmlOp(rsc, rsc_op, node, "maintenance mode", data_set);
} else if(is_probe || safe_str_eq(task, RSC_START) || interval > 0) {
check_action_definition(rsc, node, rsc_op, data_set);
}
}
g_list_free(sorted_op_list);
}
static GListPtr
find_rsc_list(
GListPtr result, resource_t *rsc, const char *id, gboolean renamed_clones, gboolean partial,
pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
gboolean match = FALSE;
if(id == NULL) {
return NULL;
} else if(rsc == NULL && data_set) {
for(gIter = data_set->resources; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
result = find_rsc_list(result, child, id, renamed_clones, partial, NULL);
}
return result;
} else if(rsc == NULL) {
return NULL;
}
if(partial) {
if(strstr(rsc->id, id)) {
match = TRUE;
} else if(rsc->long_name && strstr(rsc->long_name, id)) {
match = TRUE;
} else if(renamed_clones && rsc->clone_name && strstr(rsc->clone_name, id)) {
match = TRUE;
}
} else {
if(strcmp(rsc->id, id) == 0){
match = TRUE;
} else if(rsc->long_name && strcmp(rsc->long_name, id) == 0) {
match = TRUE;
} else if(renamed_clones && rsc->clone_name && strcmp(rsc->clone_name, id) == 0) {
match = TRUE;
}
}
if(match) {
result = g_list_prepend(result, rsc);
}
if(rsc->children) {
gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
result = find_rsc_list(result, child, id, renamed_clones, partial, NULL);
}
}
return result;
}
static void
check_actions(pe_working_set_t *data_set)
{
const char *id = NULL;
node_t *node = NULL;
xmlNode *lrm_rscs = NULL;
xmlNode *status = get_object_root(XML_CIB_TAG_STATUS, data_set->input);
xmlNode *node_state = NULL;
for(node_state = __xml_first_child(status); node_state != NULL; node_state = __xml_next(node_state)) {
if(crm_str_eq((const char *)node_state->name, XML_CIB_TAG_STATE, TRUE)) {
id = crm_element_value(node_state, XML_ATTR_ID);
lrm_rscs = find_xml_node(node_state, XML_CIB_TAG_LRM, FALSE);
lrm_rscs = find_xml_node(lrm_rscs, XML_LRM_TAG_RESOURCES, FALSE);
node = pe_find_node_id(data_set->nodes, id);
if(node == NULL) {
continue;
} else if(can_run_resources(node) == FALSE) {
crm_debug_2("Skipping param check for %s: cant run resources",
node->details->uname);
continue;
}
crm_debug_2("Processing node %s", node->details->uname);
if(node->details->online || is_set(data_set->flags, pe_flag_stonith_enabled)) {
xmlNode *rsc_entry = NULL;
for(rsc_entry = __xml_first_child(lrm_rscs); rsc_entry != NULL; rsc_entry = __xml_next(rsc_entry)) {
if(crm_str_eq((const char *)rsc_entry->name, XML_LRM_TAG_RESOURCE, TRUE)) {
if(xml_has_children(rsc_entry)) {
GListPtr gIter = NULL;
GListPtr result = NULL;
const char *rsc_id = ID(rsc_entry);
CRM_CHECK(rsc_id != NULL, return);
result = find_rsc_list(NULL, NULL, rsc_id, TRUE, FALSE, data_set);
for(gIter = result; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
check_actions_for(rsc_entry, rsc, node, data_set);
}
g_list_free(result);
}
}
}
}
}
}
}
static gboolean
apply_placement_constraints(pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
crm_debug_3("Applying constraints...");
for(gIter = data_set->placement_constraints; gIter != NULL; gIter = gIter->next) {
rsc_to_node_t *cons = (rsc_to_node_t*)gIter->data;
cons->rsc_lh->cmds->rsc_location(cons->rsc_lh, cons);
}
return TRUE;
}
static void
common_apply_stickiness(resource_t *rsc, node_t *node, pe_working_set_t *data_set)
{
int fail_count = 0;
resource_t *failed = rsc;
if(rsc->children) {
GListPtr gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
common_apply_stickiness(child_rsc, node, data_set);
}
return;
}
if(is_set(rsc->flags, pe_rsc_managed)
&& rsc->stickiness != 0
&& g_list_length(rsc->running_on) == 1) {
node_t *current = pe_find_node_id(rsc->running_on, node->details->id);
node_t *match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
if(current == NULL) {
} else if(match != NULL || is_set(data_set->flags, pe_flag_symmetric_cluster)) {
resource_t *sticky_rsc = rsc;
resource_location(sticky_rsc, node, rsc->stickiness, "stickiness", data_set);
crm_debug("Resource %s: preferring current location"
" (node=%s, weight=%d)", sticky_rsc->id,
node->details->uname, rsc->stickiness);
} else {
GHashTableIter iter;
node_t *nIter = NULL;
crm_debug("Ignoring stickiness for %s: the cluster is asymmetric"
" and node %s is not explicitly allowed",
rsc->id, node->details->uname);
g_hash_table_iter_init (&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next (&iter, NULL, (void**)&nIter)) {
crm_err("%s[%s] = %d", rsc->id, nIter->details->uname, nIter->weight);
}
}
}
if(is_not_set(rsc->flags, pe_rsc_unique)) {
failed = uber_parent(rsc);
}
fail_count = get_failcount(node, rsc, NULL, data_set);
if(fail_count > 0 && rsc->migration_threshold != 0) {
if(rsc->migration_threshold <= fail_count) {
resource_location(failed, node, -INFINITY, "__fail_limit__", data_set);
crm_warn("Forcing %s away from %s after %d failures (max=%d)",
failed->id, node->details->uname, fail_count, rsc->migration_threshold);
} else {
crm_notice("%s can fail %d more times on %s before being forced off",
failed->id, rsc->migration_threshold - fail_count, node->details->uname);
}
}
}
static void complex_set_cmds(resource_t *rsc)
{
GListPtr gIter = rsc->children;
rsc->cmds = &resource_class_alloc_functions[rsc->variant];
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
complex_set_cmds(child_rsc);
}
}
void
set_alloc_actions(pe_working_set_t *data_set)
{
GListPtr gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
complex_set_cmds(rsc);
}
}
static void
calculate_system_health (gpointer gKey, gpointer gValue, gpointer user_data)
{
const char *key = (const char *)gKey;
const char *value = (const char *)gValue;
int *system_health = (int *)user_data;
if (!gKey || !gValue || !user_data) {
return;
}
/* Does it start with #health? */
if (0 == strncmp (key, "#health", 7)) {
int score;
/* Convert the value into an integer */
score = char2score (value);
/* Add it to the running total */
*system_health = merge_weights (score, *system_health);
}
}
static gboolean
apply_system_health(pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
const char *health_strategy = pe_pref(data_set->config_hash, "node-health-strategy");
if (health_strategy == NULL
|| safe_str_eq (health_strategy, "none")) {
/* Prevent any accidental health -> score translation */
node_score_red = 0;
node_score_yellow = 0;
node_score_green = 0;
return TRUE;
} else if (safe_str_eq (health_strategy, "migrate-on-red")) {
/* Resources on nodes which have health values of red are
* weighted away from that node.
*/
node_score_red = -INFINITY;
node_score_yellow = 0;
node_score_green = 0;
} else if (safe_str_eq (health_strategy, "only-green")) {
/* Resources on nodes which have health values of red or yellow
* are forced away from that node.
*/
node_score_red = -INFINITY;
node_score_yellow = -INFINITY;
node_score_green = 0;
} else if (safe_str_eq (health_strategy, "progressive")) {
/* Same as the above, but use the r/y/g scores provided by the user
* Defaults are provided by the pe_prefs table
*/
} else if (safe_str_eq (health_strategy, "custom")) {
/* Requires the admin to configure the rsc_location constaints for
* processing the stored health scores
*/
/* TODO: Check for the existance of appropriate node health constraints */
return TRUE;
} else {
crm_err ("Unknown node health strategy: %s", health_strategy);
return FALSE;
}
crm_info ("Applying automated node health strategy: %s", health_strategy);
for(gIter = data_set->nodes; gIter != NULL; gIter = gIter->next) {
int system_health = 0;
node_t *node = (node_t*)gIter->data;
/* Search through the node hash table for system health entries. */
g_hash_table_foreach (
node->details->attrs, calculate_system_health, &system_health);
crm_info (" Node %s has an combined system health of %d",
node->details->uname, system_health);
/* If the health is non-zero, then create a new rsc2node so that the
* weight will be added later on.
*/
if (system_health != 0) {
GListPtr gIter2 = data_set->resources;
for(; gIter2 != NULL; gIter2 = gIter2->next) {
resource_t *rsc = (resource_t*)gIter2->data;
rsc2node_new (health_strategy, rsc, system_health, node, data_set);
}
}
}
return TRUE;
}
gboolean
stage0(pe_working_set_t *data_set)
{
xmlNode * cib_constraints = get_object_root(
XML_CIB_TAG_CONSTRAINTS, data_set->input);
if(data_set->input == NULL) {
return FALSE;
}
if(is_set(data_set->flags, pe_flag_have_status) == FALSE) {
crm_trace("Calculating status");
cluster_status(data_set);
}
set_alloc_actions(data_set);
apply_system_health(data_set);
unpack_constraints(cib_constraints, data_set);
return TRUE;
}
static void wait_for_probe(
resource_t *rsc, const char *action, action_t *probe_complete, pe_working_set_t *data_set)
{
if(probe_complete == NULL) {
return;
}
if(rsc->children) {
GListPtr gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
wait_for_probe(child, action, probe_complete, data_set);
}
} else {
char *key = generate_op_key(rsc->id, action, 0);
custom_action_order(
NULL, NULL, probe_complete, rsc, key, NULL,
pe_order_optional, data_set);
}
}
/*
* Check nodes for resources started outside of the LRM
*/
gboolean
probe_resources(pe_working_set_t *data_set)
{
action_t *probe_complete = NULL;
action_t *probe_node_complete = NULL;
GListPtr gIter = NULL;
GListPtr gIter2 = NULL;
gIter = data_set->nodes;
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
const char *probed = g_hash_table_lookup(
node->details->attrs, CRM_OP_PROBED);
if(node->details->online == FALSE) {
continue;
} else if(node->details->unclean) {
continue;
} else if(probe_complete == NULL) {
probe_complete = get_pseudo_op(CRM_OP_PROBED, data_set);
}
if(probed != NULL && crm_is_true(probed) == FALSE) {
action_t *probe_op = custom_action(
NULL, crm_strdup(CRM_OP_REPROBE),
CRM_OP_REPROBE, node, FALSE, TRUE, data_set);
add_hash_param(probe_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
continue;
}
probe_node_complete = custom_action(
NULL, crm_strdup(CRM_OP_PROBED),
CRM_OP_PROBED, node, FALSE, TRUE, data_set);
if(crm_is_true(probed)) {
crm_trace("unset");
update_action_flags(probe_node_complete, pe_action_optional);
} else {
crm_trace("set");
update_action_flags(probe_node_complete, pe_action_optional|pe_action_clear);
}
crm_trace("%s - %d", node->details->uname, probe_node_complete->flags & pe_action_optional);
probe_node_complete->priority = INFINITY;
add_hash_param(probe_node_complete->meta,
XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
if(node->details->pending) {
update_action_flags(probe_node_complete, pe_action_runnable|pe_action_clear);
crm_info("Action %s on %s is unrunnable (pending)",
probe_node_complete->uuid, probe_node_complete->node->details->uname);
}
order_actions(probe_node_complete, probe_complete, pe_order_runnable_left/*|pe_order_implies_then*/);
gIter2 = data_set->resources;
for(; gIter2 != NULL; gIter2 = gIter2->next) {
resource_t *rsc = (resource_t*)gIter2->data;
if(rsc->cmds->create_probe(
rsc, node, probe_node_complete,
FALSE, data_set)) {
update_action_flags(probe_complete, pe_action_optional|pe_action_clear);
update_action_flags(probe_node_complete, pe_action_optional|pe_action_clear);
wait_for_probe(rsc, CRMD_ACTION_START, probe_complete, data_set);
}
}
}
gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
wait_for_probe(rsc, CRMD_ACTION_STOP, probe_complete, data_set);
}
return TRUE;
}
/*
* Count how many valid nodes we have (so we know the maximum number of
* colors we can resolve).
*
* Apply node constraints (ie. filter the "allowed_nodes" part of resources
*/
gboolean
stage2(pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
crm_debug_3("Applying placement constraints");
gIter = data_set->nodes;
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
if(node == NULL) {
/* error */
} else if(node->weight >= 0.0 /* global weight */
&& node->details->online
&& node->details->type == node_member) {
data_set->max_valid_nodes++;
}
}
apply_placement_constraints(data_set);
gIter = data_set->nodes;
for(; gIter != NULL; gIter = gIter->next) {
GListPtr gIter2 = NULL;
node_t *node = (node_t*)gIter->data;
gIter2 = data_set->resources;
for(; gIter2 != NULL; gIter2 = gIter2->next) {
resource_t *rsc = (resource_t*)gIter2->data;
common_apply_stickiness(rsc, node, data_set);
}
}
return TRUE;
}
/*
* Create internal resource constraints before allocation
*/
gboolean
stage3(pe_working_set_t *data_set)
{
GListPtr gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
rsc->cmds->internal_constraints(rsc, data_set);
}
return TRUE;
}
/*
* Check for orphaned or redefined actions
*/
gboolean
stage4(pe_working_set_t *data_set)
{
check_actions(data_set);
return TRUE;
}
static gint
sort_rsc_process_order(gconstpointer a, gconstpointer b, gpointer data)
{
int rc = 0;
int level = LOG_TRACE;
int r1_weight = -INFINITY;
int r2_weight = -INFINITY;
const char *reason = "existance";
const GListPtr nodes = (GListPtr)data;
resource_t *resource1 = (resource_t*)convert_const_pointer(a);
resource_t *resource2 = (resource_t*)convert_const_pointer(b);
node_t *node = NULL;
GListPtr gIter = NULL;
GHashTable *r1_nodes = NULL;
GHashTable *r2_nodes = NULL;
if(a == NULL && b == NULL) { goto done; }
if(a == NULL) { return 1; }
if(b == NULL) { return -1; }
reason = "priority";
r1_weight = resource1->priority;
r2_weight = resource2->priority;
if(r1_weight > r2_weight) {
rc = -1; goto done;
}
if(r1_weight < r2_weight) {
rc = 1; goto done;
}
reason = "no node list";
if (nodes == NULL) {
goto done;
}
r1_nodes = rsc_merge_weights(resource1, resource1->id, NULL, NULL, 1, pe_weights_forward|pe_weights_init);
dump_node_scores(LOG_TRACE, NULL, resource1->id, r1_nodes);
r2_nodes = rsc_merge_weights(resource2, resource2->id, NULL, NULL, 1, pe_weights_forward|pe_weights_init);
dump_node_scores(LOG_TRACE, NULL, resource2->id, r2_nodes);
reason = "score";
for(gIter = nodes; gIter != NULL; gIter = gIter->next) {
node_t *r1_node = NULL;
node_t *r2_node = NULL;
node = (node_t*)gIter->data;
r1_weight = -INFINITY;
if(r1_nodes) {
r1_node = g_hash_table_lookup(r1_nodes, node->details->id);
}
if (r1_node) {
r1_weight = r1_node->weight;
}
r2_weight = -INFINITY;
if(r2_nodes) {
r2_node = g_hash_table_lookup(r2_nodes, node->details->id);
}
if (r2_node) {
r2_weight = r2_node->weight;
}
if(r1_weight > r2_weight) {
rc = -1; goto done;
}
if(r1_weight < r2_weight) {
rc = 1; goto done;
}
}
done:
if(r1_nodes) {
g_hash_table_destroy(r1_nodes);
}
if(r2_nodes) {
g_hash_table_destroy(r2_nodes);
}
do_crm_log_unlikely(level, "%s (%d) %c %s (%d) on %s: %s",
resource1->id, r1_weight, rc<0?'>':rc>0?'<':'=',
resource2->id, r2_weight, node?node->details->id:"n/a", reason);
return rc;
}
gboolean
stage5(pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
if (safe_str_neq(data_set->placement_strategy, "default")) {
GListPtr nodes = g_list_copy(data_set->nodes);
nodes = g_list_sort_with_data(nodes, sort_node_weight, NULL);
data_set->resources = g_list_sort_with_data(
data_set->resources, sort_rsc_process_order, nodes);
g_list_free(nodes);
}
gIter = data_set->nodes;
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
dump_node_capacity(show_utilization?0:utilization_log_level, "Original", node);
}
crm_trace("Allocating services");
/* Take (next) highest resource, assign it and create its actions */
gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
+ crm_trace("Allocating: %s", rsc->id);
rsc->cmds->allocate(rsc, NULL, data_set);
}
gIter = data_set->nodes;
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
dump_node_capacity(show_utilization?0:utilization_log_level, "Remaining", node);
}
if(is_set(data_set->flags, pe_flag_startup_probes)) {
crm_trace("Calculating needed probes");
/* This code probably needs optimization
* ptest -x with 100 nodes, 100 clones and clone-max=100:
With probes:
ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status
ptest[14781]: 2010/09/27_17:56:46 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints
ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints
ptest[14781]: 2010/09/27_17:56:47 notice: TRACE: do_calculations: pengine.c:292 Check actions
ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: do_calculations: pengine.c:299 Allocate resources
ptest[14781]: 2010/09/27_17:56:48 notice: TRACE: stage5: allocate.c:881 Allocating services
ptest[14781]: 2010/09/27_17:56:49 notice: TRACE: stage5: allocate.c:894 Calculating needed probes
ptest[14781]: 2010/09/27_17:56:51 notice: TRACE: stage5: allocate.c:899 Creating actions
ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: stage5: allocate.c:905 Creating done
ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases
ptest[14781]: 2010/09/27_17:56:52 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints
36s
ptest[14781]: 2010/09/27_17:57:28 notice: TRACE: do_calculations: pengine.c:320 Create transition graph
Without probes:
ptest[14637]: 2010/09/27_17:56:21 notice: TRACE: do_calculations: pengine.c:258 Calculate cluster status
ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:278 Applying placement constraints
ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:285 Create internal constraints
ptest[14637]: 2010/09/27_17:56:22 notice: TRACE: do_calculations: pengine.c:292 Check actions
ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: do_calculations: pengine.c:299 Allocate resources
ptest[14637]: 2010/09/27_17:56:23 notice: TRACE: stage5: allocate.c:881 Allocating services
ptest[14637]: 2010/09/27_17:56:24 notice: TRACE: stage5: allocate.c:899 Creating actions
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: stage5: allocate.c:905 Creating done
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:306 Processing fencing and shutdown cases
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:313 Applying ordering constraints
ptest[14637]: 2010/09/27_17:56:25 notice: TRACE: do_calculations: pengine.c:320 Create transition graph
*/
probe_resources(data_set);
}
crm_trace("Creating actions");
gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
rsc->cmds->create_actions(rsc, data_set);
}
crm_trace("Creating done");
return TRUE;
}
static gboolean is_managed(const resource_t *rsc)
{
GListPtr gIter = rsc->children;
if(is_set(rsc->flags, pe_rsc_managed)) {
return TRUE;
}
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
if(is_managed(child_rsc)) {
return TRUE;
}
}
return FALSE;
}
static gboolean any_managed_resouces(pe_working_set_t *data_set)
{
GListPtr gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
if(is_managed(rsc)) {
return TRUE;
}
}
return FALSE;
}
/*
* Create dependancies for stonith and shutdown operations
*/
gboolean
stage6(pe_working_set_t *data_set)
{
action_t *dc_down = NULL;
action_t *dc_fence = NULL;
action_t *stonith_op = NULL;
action_t *last_stonith = NULL;
gboolean integrity_lost = FALSE;
action_t *ready = get_pseudo_op(STONITH_UP, data_set);
action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
action_t *done = get_pseudo_op(STONITH_DONE, data_set);
gboolean need_stonith = FALSE;
GListPtr gIter = data_set->nodes;
crm_debug_3("Processing fencing and shutdown cases");
if(is_set(data_set->flags, pe_flag_stonith_enabled)
&& (is_set(data_set->flags, pe_flag_have_quorum)
|| data_set->no_quorum_policy == no_quorum_ignore
|| data_set->no_quorum_policy == no_quorum_suicide)) {
need_stonith = TRUE;
}
if(need_stonith && any_managed_resouces(data_set) == FALSE) {
crm_notice("Delaying fencing operations until there are resources to manage");
need_stonith = FALSE;
}
for(; gIter != NULL; gIter = gIter->next) {
node_t *node = (node_t*)gIter->data;
stonith_op = NULL;
if(node->details->unclean && need_stonith) {
pe_warn("Scheduling Node %s for STONITH",
node->details->uname);
stonith_op = custom_action(
NULL, crm_strdup(CRM_OP_FENCE),
CRM_OP_FENCE, node, FALSE, TRUE, data_set);
add_hash_param(
stonith_op->meta, XML_LRM_ATTR_TARGET,
node->details->uname);
add_hash_param(
stonith_op->meta, XML_LRM_ATTR_TARGET_UUID,
node->details->id);
add_hash_param(
stonith_op->meta, "stonith_action",
data_set->stonith_action);
stonith_constraints(node, stonith_op, data_set);
order_actions(ready, stonith_op, pe_order_runnable_left);
order_actions(stonith_op, all_stopped, pe_order_implies_then);
clear_bit_inplace(ready->flags, pe_action_optional);
if(node->details->is_dc) {
dc_down = stonith_op;
dc_fence = stonith_op;
} else {
if(last_stonith) {
order_actions(last_stonith, stonith_op, pe_order_optional);
}
last_stonith = stonith_op;
}
} else if(node->details->online && node->details->shutdown) {
action_t *down_op = NULL;
crm_notice("Scheduling Node %s for shutdown",
node->details->uname);
down_op = custom_action(
NULL, crm_strdup(CRM_OP_SHUTDOWN),
CRM_OP_SHUTDOWN, node, FALSE, TRUE, data_set);
shutdown_constraints(node, down_op, data_set);
add_hash_param(down_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
if(node->details->is_dc) {
dc_down = down_op;
}
}
if(node->details->unclean && stonith_op == NULL) {
integrity_lost = TRUE;
pe_warn("Node %s is unclean!", node->details->uname);
}
}
if(integrity_lost) {
if(is_set(data_set->flags, pe_flag_stonith_enabled) == FALSE) {
pe_warn("YOUR RESOURCES ARE NOW LIKELY COMPROMISED");
pe_err("ENABLE STONITH TO KEEP YOUR RESOURCES SAFE");
} else if(is_set(data_set->flags, pe_flag_have_quorum) == FALSE) {
crm_notice("Cannot fence unclean nodes until quorum is"
" attained (or no-quorum-policy is set to ignore)");
}
}
if(dc_down != NULL) {
GListPtr shutdown_matches = find_actions(
data_set->actions, CRM_OP_SHUTDOWN, NULL);
crm_debug_2("Ordering shutdowns before %s on %s (DC)",
dc_down->task, dc_down->node->details->uname);
add_hash_param(dc_down->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
gIter = shutdown_matches;
for(; gIter != NULL; gIter = gIter->next) {
action_t *node_stop = (action_t*)gIter->data;
if(node_stop->node->details->is_dc) {
continue;
}
crm_debug("Ordering shutdown on %s before %s on %s",
node_stop->node->details->uname,
dc_down->task, dc_down->node->details->uname);
order_actions(node_stop, dc_down, pe_order_optional);
}
if(last_stonith && dc_down != last_stonith) {
order_actions(last_stonith, dc_down, pe_order_optional);
}
g_list_free(shutdown_matches);
}
if(last_stonith) {
order_actions(last_stonith, done, pe_order_implies_then);
} else if(dc_fence) {
order_actions(dc_down, done, pe_order_implies_then);
}
order_actions(ready, done, pe_order_optional);
return TRUE;
}
/*
* Determin the sets of independant actions and the correct order for the
* actions in each set.
*
* Mark dependencies of un-runnable actions un-runnable
*
*/
static GListPtr find_actions_by_task(GListPtr actions, resource_t *rsc, const char *original_key)
{
GListPtr list = NULL;
list = find_actions(actions, original_key, NULL);
if(list == NULL) {
/* we're potentially searching a child of the original resource */
char *key = NULL;
char *tmp = NULL;
char *task = NULL;
int interval = 0;
if(parse_op_key(original_key, &tmp, &task, &interval)) {
key = generate_op_key(rsc->id, task, interval);
/* crm_err("looking up %s instead of %s", key, original_key); */
/* slist_iter(action, action_t, actions, lpc, */
/* crm_err(" - %s", action->uuid)); */
list = find_actions(actions, key, NULL);
} else {
crm_err("search key: %s", original_key);
}
crm_free(key);
crm_free(tmp);
crm_free(task);
}
return list;
}
static void rsc_order_then(
action_t *lh_action, resource_t *rsc, order_constraint_t *order)
{
GListPtr gIter = NULL;
GListPtr rh_actions = NULL;
action_t *rh_action = NULL;
enum pe_ordering type = order->type;
CRM_CHECK(rsc != NULL, return);
CRM_CHECK(order != NULL, return);
rh_action = order->rh_action;
crm_debug_3("Processing RH of ordering constraint %d", order->id);
if(rh_action != NULL) {
rh_actions = g_list_prepend(NULL, rh_action);
} else if(rsc != NULL) {
rh_actions = find_actions_by_task(
rsc->actions, rsc, order->rh_action_task);
}
if(rh_actions == NULL) {
crm_debug_4("No RH-Side (%s/%s) found for constraint..."
" ignoring", rsc->id,order->rh_action_task);
if(lh_action) {
crm_debug_4("LH-Side was: %s", lh_action->uuid);
}
return;
}
if(lh_action->rsc == rsc
&& is_set(lh_action->flags, pe_action_dangle)) {
crm_trace("Detected dangling operation %s -> %s",
lh_action->uuid, order->rh_action_task);
clear_bit_inplace(type, pe_order_implies_then);
}
gIter = rh_actions;
for(; gIter != NULL; gIter = gIter->next) {
action_t *rh_action_iter = (action_t*)gIter->data;
if(lh_action) {
order_actions(lh_action, rh_action_iter, type);
} else if(type & pe_order_implies_then) {
update_action_flags(rh_action_iter, pe_action_runnable|pe_action_clear);
crm_warn("Unrunnable %s 0x%.6x", rh_action_iter->uuid, type);
} else {
crm_warn("neither %s 0x%.6x", rh_action_iter->uuid, type);
}
}
g_list_free(rh_actions);
}
static void rsc_order_first(resource_t *lh_rsc, order_constraint_t *order, pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
GListPtr lh_actions = NULL;
action_t *lh_action = order->lh_action;
resource_t *rh_rsc = order->rh_rsc;
crm_debug_3("Processing LH of ordering constraint %d", order->id);
CRM_ASSERT(lh_rsc != NULL);
if(lh_action != NULL) {
lh_actions = g_list_prepend(NULL, lh_action);
} else if(lh_action == NULL) {
lh_actions = find_actions_by_task(
lh_rsc->actions, lh_rsc, order->lh_action_task);
}
if(lh_actions == NULL && lh_rsc != rh_rsc) {
char *key = NULL;
char *rsc_id = NULL;
char *op_type = NULL;
int interval = 0;
parse_op_key(order->lh_action_task, &rsc_id, &op_type, &interval);
key = generate_op_key(lh_rsc->id, op_type, interval);
if(lh_rsc->fns->state(lh_rsc, TRUE) != RSC_ROLE_STOPPED
|| safe_str_neq(op_type, RSC_STOP)) {
crm_debug_4("No LH-Side (%s/%s) found for constraint %d with %s - creating",
lh_rsc->id, order->lh_action_task,
order->id, order->rh_action_task);
lh_action = custom_action(lh_rsc, key, op_type,
NULL, TRUE, TRUE, data_set);
lh_actions = g_list_prepend(NULL, lh_action);
} else {
crm_free(key);
crm_debug_4("No LH-Side (%s/%s) found for constraint %d with %s - ignoring",
lh_rsc->id, order->lh_action_task,
order->id, order->rh_action_task);
}
crm_free(op_type);
crm_free(rsc_id);
}
gIter = lh_actions;
for(; gIter != NULL; gIter = gIter->next) {
action_t *lh_action_iter = (action_t*)gIter->data;
if(rh_rsc == NULL && order->rh_action) {
rh_rsc = order->rh_action->rsc;
}
if(rh_rsc) {
rsc_order_then(lh_action_iter, rh_rsc, order);
} else if(order->rh_action) {
order_actions(lh_action_iter, order->rh_action, order->type);
}
}
g_list_free(lh_actions);
}
extern gboolean update_action(action_t *action);
gboolean
stage7(pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
crm_debug_4("Applying ordering constraints");
/* Don't ask me why, but apparently they need to be processed in
* the order they were created in... go figure
*
* Also g_list_prepend() has horrendous performance characteristics
* So we need to use g_list_prepend() and then reverse the list here
*/
data_set->ordering_constraints = g_list_reverse(
data_set->ordering_constraints);
gIter = data_set->ordering_constraints;
for(; gIter != NULL; gIter = gIter->next) {
order_constraint_t *order = (order_constraint_t*)gIter->data;
resource_t *rsc = order->lh_rsc;
crm_debug_3("Applying ordering constraint: %d", order->id);
if(rsc != NULL) {
crm_debug_4("rsc_action-to-*");
rsc_order_first(rsc, order, data_set);
continue;
}
rsc = order->rh_rsc;
if(rsc != NULL) {
crm_debug_4("action-to-rsc_action");
rsc_order_then(order->lh_action, rsc, order);
} else {
crm_debug_4("action-to-action");
order_actions(
order->lh_action, order->rh_action, order->type);
}
}
crm_debug_2("Updating %d actions", g_list_length(data_set->actions));
gIter = data_set->actions;
for(; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t*)gIter->data;
update_action(action);
}
crm_debug_2("Processing migrations");
gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
rsc_migrate_reload(rsc, data_set);
LogActions(rsc, data_set);
}
return TRUE;
}
static gint
sort_notify_entries(gconstpointer a, gconstpointer b)
{
int tmp;
const notify_entry_t *entry_a = a;
const notify_entry_t *entry_b = b;
if(entry_a == NULL && entry_b == NULL) { return 0; }
if(entry_a == NULL) { return 1; }
if(entry_b == NULL) { return -1; }
if(entry_a->rsc == NULL && entry_b->rsc == NULL) { return 0; }
if(entry_a->rsc == NULL) { return 1; }
if(entry_b->rsc == NULL) { return -1; }
tmp = strcmp(entry_a->rsc->id, entry_b->rsc->id);
if(tmp != 0) {
return tmp;
}
if(entry_a->node == NULL && entry_b->node == NULL) { return 0; }
if(entry_a->node == NULL) { return 1; }
if(entry_b->node == NULL) { return -1; }
return strcmp(entry_a->node->details->id, entry_b->node->details->id);
}
static void
expand_list(GListPtr list, char **rsc_list, char **node_list)
{
GListPtr gIter = list;
const char *uname = NULL;
const char *rsc_id = NULL;
const char *last_rsc_id = NULL;
if(list == NULL) {
*rsc_list = crm_strdup(" ");
if(node_list) {
*node_list = crm_strdup(" ");
}
return;
}
*rsc_list = NULL;
if(node_list) {
*node_list = NULL;
}
for(; gIter != NULL; gIter = gIter->next) {
notify_entry_t *entry = (notify_entry_t*)gIter->data;
CRM_CHECK(entry != NULL, continue);
CRM_CHECK(entry->rsc != NULL, continue);
CRM_CHECK(node_list == NULL || entry->node != NULL, continue);
uname = NULL;
rsc_id = entry->rsc->id;
CRM_ASSERT(rsc_id != NULL);
/* filter dups */
if(safe_str_eq(rsc_id, last_rsc_id)) {
continue;
}
last_rsc_id = rsc_id;
if(rsc_list != NULL) {
int existing_len = 0;
int len = 2 + strlen(rsc_id); /* +1 space, +1 EOS */
if(rsc_list && *rsc_list) {
existing_len = strlen(*rsc_list);
}
crm_debug_5("Adding %s (%dc) at offset %d",
rsc_id, len-2, existing_len);
crm_realloc(*rsc_list, len + existing_len);
sprintf(*rsc_list + existing_len, "%s ", rsc_id);
}
if(entry->node != NULL) {
uname = entry->node->details->uname;
}
if(node_list != NULL && uname) {
int existing_len = 0;
int len = 2 + strlen(uname);
if(node_list && *node_list) {
existing_len = strlen(*node_list);
}
crm_debug_5("Adding %s (%dc) at offset %d",
uname, len-2, existing_len);
crm_realloc(*node_list, len + existing_len);
sprintf(*node_list + existing_len, "%s ", uname);
}
}
}
static void dup_attr(gpointer key, gpointer value, gpointer user_data)
{
add_hash_param(user_data, key, value);
}
static action_t *
pe_notify(resource_t *rsc, node_t *node, action_t *op, action_t *confirm,
notify_data_t *n_data, pe_working_set_t *data_set)
{
char *key = NULL;
action_t *trigger = NULL;
const char *value = NULL;
const char *task = NULL;
if(op == NULL || confirm == NULL) {
crm_debug_2("Op=%p confirm=%p", op, confirm);
return NULL;
}
CRM_CHECK(node != NULL, return NULL);
if(node->details->online == FALSE) {
crm_debug_2("Skipping notification for %s: node offline", rsc->id);
return NULL;
} else if(is_set(op->flags, pe_action_runnable) == FALSE) {
crm_debug_2("Skipping notification for %s: not runnable", op->uuid);
return NULL;
}
value = g_hash_table_lookup(op->meta, "notify_type");
task = g_hash_table_lookup(op->meta, "notify_operation");
crm_debug_2("Creating notify actions for %s: %s (%s-%s)",
op->uuid, rsc->id, value, task);
key = generate_notify_key(rsc->id, value, task);
trigger = custom_action(rsc, key, op->task, node,
is_set(op->flags, pe_action_optional), TRUE, data_set);
g_hash_table_foreach(op->meta, dup_attr, trigger->meta);
g_hash_table_foreach(n_data->keys, dup_attr, trigger->meta);
/* pseudo_notify before notify */
crm_debug_3("Ordering %s before %s (%d->%d)",
op->uuid, trigger->uuid, trigger->id, op->id);
order_actions(op, trigger, pe_order_optional);
order_actions(trigger, confirm, pe_order_optional);
return trigger;
}
static void
pe_post_notify(resource_t *rsc, node_t *node, notify_data_t *n_data, pe_working_set_t *data_set)
{
action_t *notify = NULL;
CRM_CHECK(rsc != NULL, return);
if(n_data->post == NULL) {
return; /* Nothing to do */
}
notify = pe_notify(rsc, node, n_data->post, n_data->post_done, n_data, data_set);
if(notify != NULL) {
notify->priority = INFINITY;
}
if(n_data->post_done) {
GListPtr gIter = rsc->actions;
for(; gIter != NULL; gIter = gIter->next) {
action_t *mon = (action_t*)gIter->data;
const char *interval = g_hash_table_lookup(mon->meta, "interval");
if(interval == NULL || safe_str_eq(interval, "0")) {
crm_debug_3("Skipping %s: interval", mon->uuid);
continue;
} else if(safe_str_eq(mon->task, "cancel")) {
crm_debug_3("Skipping %s: cancel", mon->uuid);
continue;
}
order_actions(n_data->post_done, mon, pe_order_optional);
}
}
}
notify_data_t *
create_notification_boundaries(
resource_t *rsc, const char *action, action_t *start, action_t *end, pe_working_set_t *data_set)
{
/* Create the pseudo ops that preceed and follow the actual notifications */
/*
* Creates two sequences (conditional on start and end being supplied):
* pre_notify -> pre_notify_complete -> start, and
* end -> post_notify -> post_notify_complete
*
* 'start' and 'end' may be the same event or ${X} and ${X}ed as per clones
*/
char *key = NULL;
notify_data_t *n_data = NULL;
if(is_not_set(rsc->flags, pe_rsc_notify)) {
return NULL;
}
crm_malloc0(n_data, sizeof(notify_data_t));
n_data->action = action;
n_data->keys = g_hash_table_new_full(
crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
if(start) {
/* create pre-event notification wrappers */
key = generate_notify_key(rsc->id, "pre", start->task);
n_data->pre = custom_action(
rsc, key, RSC_NOTIFY, NULL, is_set(start->flags, pe_action_optional), TRUE, data_set);
update_action_flags(n_data->pre, pe_action_pseudo);
update_action_flags(n_data->pre, pe_action_runnable);
add_hash_param(n_data->pre->meta, "notify_type", "pre");
add_hash_param(n_data->pre->meta, "notify_operation", n_data->action);
/* create pre_notify_complete */
key = generate_notify_key(rsc->id, "confirmed-pre", start->task);
n_data->pre_done = custom_action(
rsc, key, RSC_NOTIFIED, NULL, is_set(start->flags, pe_action_optional), TRUE, data_set);
update_action_flags(n_data->pre_done, pe_action_pseudo);
update_action_flags(n_data->pre_done, pe_action_runnable);
add_hash_param(n_data->pre_done->meta, "notify_type", "pre");
add_hash_param(n_data->pre_done->meta, "notify_operation", n_data->action);
order_actions(n_data->pre_done, start, pe_order_optional);
order_actions(n_data->pre, n_data->pre_done, pe_order_optional);
}
if(end) {
/* create post-event notification wrappers */
key = generate_notify_key(rsc->id, "post", end->task);
n_data->post = custom_action(
rsc, key, RSC_NOTIFY, NULL, is_set(end->flags, pe_action_optional), TRUE, data_set);
n_data->post->priority = INFINITY;
update_action_flags(n_data->post, pe_action_pseudo);
if(is_set(end->flags, pe_action_runnable)) {
update_action_flags(n_data->post, pe_action_runnable);
} else {
update_action_flags(n_data->post, pe_action_runnable|pe_action_clear);
}
add_hash_param(n_data->post->meta, "notify_type", "post");
add_hash_param(n_data->post->meta, "notify_operation", n_data->action);
/* create post_notify_complete */
key = generate_notify_key(rsc->id, "confirmed-post", end->task);
n_data->post_done = custom_action(
rsc, key, RSC_NOTIFIED, NULL, is_set(end->flags, pe_action_optional), TRUE, data_set);
n_data->post_done->priority = INFINITY;
update_action_flags(n_data->post_done, pe_action_pseudo);
if(is_set(end->flags, pe_action_runnable)) {
update_action_flags(n_data->post_done, pe_action_runnable);
} else {
update_action_flags(n_data->post_done, pe_action_runnable|pe_action_clear);
}
add_hash_param(n_data->post_done->meta, "notify_type", "pre");
add_hash_param(n_data->post_done->meta, "notify_operation", n_data->action);
order_actions(end, n_data->post, pe_order_implies_then);
order_actions(n_data->post, n_data->post_done, pe_order_implies_then);
}
if(start && end) {
order_actions(n_data->pre_done, n_data->post, pe_order_optional);
}
if(safe_str_eq(action, RSC_STOP)) {
action_t *all_stopped = get_pseudo_op(ALL_STOPPED, data_set);
order_actions(n_data->post_done, all_stopped, pe_order_optional);
}
return n_data;
}
void
collect_notification_data(resource_t *rsc, gboolean state, gboolean activity, notify_data_t *n_data)
{
if(rsc->children) {
GListPtr gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
collect_notification_data(child, state, activity, n_data);
}
return;
}
if(state) {
notify_entry_t *entry = NULL;
crm_malloc0(entry, sizeof(notify_entry_t));
entry->rsc = rsc;
if(rsc->running_on) {
/* we only take the first one */
entry->node = rsc->running_on->data;
}
crm_debug_2("%s state: %s", rsc->id, role2text(rsc->role));
switch(rsc->role) {
case RSC_ROLE_STOPPED:
n_data->inactive = g_list_prepend(n_data->inactive, entry);
break;
case RSC_ROLE_STARTED:
n_data->active = g_list_prepend(n_data->active, entry);
break;
case RSC_ROLE_SLAVE:
n_data->slave = g_list_prepend(n_data->slave, entry);
break;
case RSC_ROLE_MASTER:
n_data->master = g_list_prepend(n_data->master, entry);
break;
default:
crm_err("Unsupported notify role");
crm_free(entry);
break;
}
}
if(activity) {
notify_entry_t *entry = NULL;
enum action_tasks task;
GListPtr gIter = rsc->actions;
for(; gIter != NULL; gIter = gIter->next) {
action_t *op = (action_t*)gIter->data;
if(is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) {
crm_malloc0(entry, sizeof(notify_entry_t));
entry->node = op->node;
entry->rsc = rsc;
task = text2task(op->task);
switch(task) {
case start_rsc:
n_data->start = g_list_prepend(n_data->start, entry);
break;
case stop_rsc:
n_data->stop = g_list_prepend(n_data->stop, entry);
break;
case action_promote:
n_data->promote = g_list_prepend(n_data->promote, entry);
break;
case action_demote:
n_data->demote = g_list_prepend(n_data->demote, entry);
break;
default:
crm_free(entry);
break;
}
}
}
}
}
gboolean
expand_notification_data(notify_data_t *n_data)
{
/* Expand the notification entries into a key=value hashtable
* This hashtable is later used in action2xml()
*/
gboolean required = FALSE;
char *rsc_list = NULL;
char *node_list = NULL;
if(n_data->stop) {
n_data->stop = g_list_sort(n_data->stop, sort_notify_entries);
}
expand_list(n_data->stop, &rsc_list, &node_list);
if(rsc_list != NULL && safe_str_neq(" ", rsc_list)) {
if(safe_str_eq(n_data->action, RSC_STOP)) {
required = TRUE;
}
}
g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_stop_uname"), node_list);
if(n_data->start) {
n_data->start = g_list_sort(n_data->start, sort_notify_entries);
if(rsc_list && safe_str_eq(n_data->action, RSC_START)) {
required = TRUE;
}
}
expand_list(n_data->start, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_start_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_start_uname"), node_list);
if(n_data->demote) {
n_data->demote = g_list_sort(n_data->demote, sort_notify_entries);
if(safe_str_eq(n_data->action, RSC_DEMOTE)) {
required = TRUE;
}
}
expand_list(n_data->demote, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_demote_uname"), node_list);
if(n_data->promote) {
n_data->promote = g_list_sort(n_data->promote, sort_notify_entries);
if(safe_str_eq(n_data->action, RSC_PROMOTE)) {
required = TRUE;
}
}
expand_list(n_data->promote, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_promote_uname"), node_list);
if(n_data->active) {
n_data->active = g_list_sort(n_data->active, sort_notify_entries);
}
expand_list(n_data->active, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_active_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_active_uname"), node_list);
if(n_data->slave) {
n_data->slave = g_list_sort(n_data->slave, sort_notify_entries);
}
expand_list(n_data->slave, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_slave_uname"), node_list);
if(n_data->master) {
n_data->master = g_list_sort(n_data->master, sort_notify_entries);
}
expand_list(n_data->master, &rsc_list, &node_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_master_resource"), rsc_list);
g_hash_table_insert(n_data->keys, crm_strdup("notify_master_uname"), node_list);
if(n_data->inactive) {
n_data->inactive = g_list_sort(n_data->inactive, sort_notify_entries);
}
expand_list(n_data->inactive, &rsc_list, NULL);
g_hash_table_insert(n_data->keys, crm_strdup("notify_inactive_resource"), rsc_list);
if(required && n_data->pre) {
update_action_flags(n_data->pre, pe_action_optional|pe_action_clear);
update_action_flags(n_data->pre_done, pe_action_optional|pe_action_clear);
}
if(required && n_data->post) {
update_action_flags(n_data->post, pe_action_optional|pe_action_clear);
update_action_flags(n_data->post_done, pe_action_optional|pe_action_clear);
}
return required;
}
void
create_notifications(resource_t *rsc, notify_data_t *n_data, pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
action_t *stop = NULL;
action_t *start = NULL;
enum action_tasks task = text2task(n_data->action);
if(rsc->children) {
gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
create_notifications(child, n_data, data_set);
}
return;
}
/* Copy notification details into standard ops */
gIter = rsc->actions;
for(; gIter != NULL; gIter = gIter->next) {
action_t *op = (action_t*)gIter->data;
if(is_set(op->flags, pe_action_optional) == FALSE && op->node != NULL) {
enum action_tasks t = text2task(op->task);
switch(t) {
case start_rsc:
case stop_rsc:
case action_promote:
case action_demote:
g_hash_table_foreach(n_data->keys, dup_attr, op->meta);
break;
default:
break;
}
}
}
crm_debug_2("Creating notificaitons for: %s.%s (%s->%s)",
n_data->action, rsc->id, role2text(rsc->role), role2text(rsc->next_role));
stop = find_first_action(rsc->actions, NULL, RSC_STOP, NULL);
start = find_first_action(rsc->actions, NULL, RSC_START, NULL);
/* stop / demote */
if(rsc->role != RSC_ROLE_STOPPED) {
if(task == stop_rsc || task == action_demote) {
gIter = rsc->running_on;
for(; gIter != NULL; gIter = gIter->next) {
node_t *current_node = (node_t*)gIter->data;
pe_notify(rsc, current_node, n_data->pre, n_data->pre_done, n_data, data_set);
if(task == action_demote || stop == NULL || is_set(stop->flags, pe_action_optional)) {
pe_post_notify(rsc, current_node, n_data, data_set);
}
}
}
}
/* start / promote */
if(rsc->next_role != RSC_ROLE_STOPPED) {
if(rsc->allocated_to == NULL) {
pe_proc_err("Next role '%s' but %s is not allocated", role2text(rsc->next_role), rsc->id);
} else if(task == start_rsc || task == action_promote) {
if(task != start_rsc || start == NULL || is_set(start->flags, pe_action_optional)) {
pe_notify(rsc, rsc->allocated_to, n_data->pre, n_data->pre_done, n_data, data_set);
}
pe_post_notify(rsc, rsc->allocated_to, n_data, data_set);
}
}
}
void free_notification_data(notify_data_t *n_data)
{
if(n_data == NULL) {
return;
}
slist_basic_destroy(n_data->stop);
slist_basic_destroy(n_data->start);
slist_basic_destroy(n_data->demote);
slist_basic_destroy(n_data->promote);
slist_basic_destroy(n_data->master);
slist_basic_destroy(n_data->slave);
slist_basic_destroy(n_data->active);
slist_basic_destroy(n_data->inactive);
g_hash_table_destroy(n_data->keys);
crm_free(n_data);
}
int transition_id = -1;
/*
* Create a dependency graph to send to the transitioner (via the CRMd)
*/
gboolean
stage8(pe_working_set_t *data_set)
{
GListPtr gIter = NULL;
const char *value = NULL;
transition_id++;
crm_debug_2("Creating transition graph %d.", transition_id);
data_set->graph = create_xml_node(NULL, XML_TAG_GRAPH);
value = pe_pref(data_set->config_hash, "cluster-delay");
crm_xml_add(data_set->graph, "cluster-delay", value);
value = pe_pref(data_set->config_hash, "stonith-timeout");
crm_xml_add(data_set->graph, "stonith-timeout", value);
crm_xml_add(data_set->graph, "failed-stop-offset", "INFINITY");
if(is_set(data_set->flags, pe_flag_start_failure_fatal)) {
crm_xml_add(data_set->graph, "failed-start-offset", "INFINITY");
} else {
crm_xml_add(data_set->graph, "failed-start-offset", "1");
}
value = pe_pref(data_set->config_hash, "batch-limit");
crm_xml_add(data_set->graph, "batch-limit", value);
crm_xml_add_int(data_set->graph, "transition_id", transition_id);
/* errors...
slist_iter(action, action_t, action_list, lpc,
if(action->optional == FALSE && action->runnable == FALSE) {
print_action("Ignoring", action, TRUE);
}
);
*/
gIter = data_set->resources;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *rsc = (resource_t*)gIter->data;
crm_debug_4("processing actions for rsc=%s", rsc->id);
rsc->cmds->expand(rsc, data_set);
}
crm_log_xml_debug_3(
data_set->graph, "created resource-driven action list");
/* catch any non-resource specific actions */
crm_debug_4("processing non-resource actions");
gIter = data_set->actions;
for(; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t*)gIter->data;
graph_element_from_action(action, data_set);
}
crm_log_xml_debug_3(data_set->graph, "created generic action list");
crm_debug_2("Created transition graph %d.", transition_id);
return TRUE;
}
void
cleanup_alloc_calculations(pe_working_set_t *data_set)
{
if(data_set == NULL) {
return;
}
crm_debug_3("deleting %d order cons: %p",
g_list_length(data_set->ordering_constraints), data_set->ordering_constraints);
pe_free_ordering(data_set->ordering_constraints);
data_set->ordering_constraints = NULL;
crm_debug_3("deleting %d node cons: %p",
g_list_length(data_set->placement_constraints), data_set->placement_constraints);
pe_free_rsc_to_node(data_set->placement_constraints);
data_set->placement_constraints = NULL;
crm_debug_3("deleting %d inter-resource cons: %p",
g_list_length(data_set->colocation_constraints), data_set->colocation_constraints);
slist_basic_destroy(data_set->colocation_constraints);
data_set->colocation_constraints = NULL;
cleanup_calculations(data_set);
}
diff --git a/pengine/clone.c b/pengine/clone.c
index 10c78f76a1..5ae97283a0 100644
--- a/pengine/clone.c
+++ b/pengine/clone.c
@@ -1,1503 +1,1492 @@
/*
* 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 <utils.h>
#include <lib/pengine/utils.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 node_t *
parent_node_instance(const resource_t *rsc, node_t *node)
{
node_t *ret = NULL;
if(node != NULL) {
ret = pe_hash_table_lookup(rsc->parent->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;
int level = LOG_DEBUG_3;
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 cant 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)) {
do_crm_log_unlikely(level, "%s < %s: running_on", resource1->id, resource2->id);
return -1;
} else if(g_list_length(resource1->running_on) > g_list_length(resource2->running_on)) {
do_crm_log_unlikely(level, "%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) {
do_crm_log_unlikely(level, "%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) {
do_crm_log_unlikely(level, "%s: current location is unavailable", resource2->id);
node2 = NULL;
can2 = FALSE;
}
}
if(can1 != can2) {
if(can1) {
do_crm_log_unlikely(level, "%s < %s: availability of current location", resource1->id, resource2->id);
return -1;
}
do_crm_log_unlikely(level, "%s > %s: availability of current location", resource1->id, resource2->id);
return 1;
}
if(resource1->priority < resource2->priority) {
do_crm_log_unlikely(level, "%s < %s: priority", resource1->id, resource2->id);
return 1;
} else if(resource1->priority > resource2->priority) {
do_crm_log_unlikely(level, "%s > %s: priority", resource1->id, resource2->id);
return -1;
}
if(node1 == NULL && node2 == NULL) {
do_crm_log_unlikely(level, "%s == %s: not active",
resource1->id, resource2->id);
return 0;
}
if(node1 != node2) {
if(node1 == NULL) {
do_crm_log_unlikely(level, "%s > %s: active", resource1->id, resource2->id);
return 1;
} else if(node2 == NULL) {
do_crm_log_unlikely(level, "%s < %s: active", resource1->id, resource2->id);
return -1;
}
}
can1 = can_run_resources(node1);
can2 = can_run_resources(node2);
if(can1 != can2) {
if(can1) {
do_crm_log_unlikely(level, "%s < %s: can", resource1->id, resource2->id);
return -1;
}
do_crm_log_unlikely(level, "%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) {
do_crm_log_unlikely(level, "%s < %s: not allowed", resource1->id, resource2->id);
return -1;
} else if(node1 == NULL && node2 != NULL) {
do_crm_log_unlikely(level, "%s > %s: not allowed", resource1->id, resource2->id);
return 1;
}
if(node1 == NULL) {
do_crm_log_unlikely(level, "%s == %s: not allowed", resource1->id, resource2->id);
return 0;
}
if(node1->count < node2->count) {
do_crm_log_unlikely(level, "%s < %s: count", resource1->id, resource2->id);
return -1;
} else if(node1->count > node2->count) {
do_crm_log_unlikely(level, "%s > %s: count", resource1->id, resource2->id);
return 1;
}
can1 = did_fail(resource1);
can2 = did_fail(resource2);
if(can1 != can2) {
if(can1) {
do_crm_log_unlikely(level, "%s > %s: failed", resource1->id, resource2->id);
return 1;
}
do_crm_log_unlikely(level, "%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);
for(gIter = resource1->parent->rsc_cons; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data;
do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource1->id);
hash1 = native_merge_weights(
constraint->rsc_rh, resource1->id, hash1,
constraint->node_attribute,
constraint->score/INFINITY, FALSE, FALSE);
}
for(gIter = resource1->parent->rsc_cons_lhs; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data;
do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource1->id);
hash1 = native_merge_weights(
constraint->rsc_lh, resource1->id, hash1,
constraint->node_attribute,
constraint->score/INFINITY, FALSE, TRUE);
}
for(gIter = resource2->parent->rsc_cons; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data;
do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource2->id);
hash2 = native_merge_weights(
constraint->rsc_rh, resource2->id, hash2,
constraint->node_attribute,
constraint->score/INFINITY, FALSE, FALSE);
}
for(gIter = resource2->parent->rsc_cons_lhs; gIter; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data;
do_crm_log_unlikely(level+1, "Applying %s to %s", constraint->id, resource2->id);
hash2 = native_merge_weights(
constraint->rsc_lh, resource2->id, hash2,
constraint->node_attribute,
constraint->score/INFINITY, FALSE, TRUE);
}
/* 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) {
do_crm_log_unlikely(level, "%s > %s: current score", resource1->id, resource2->id);
return -1;
} else {
do_crm_log_unlikely(level, "%s < %s: current score", resource1->id, resource2->id);
return 1;
}
} else if(node1->weight > node2->weight) {
do_crm_log_unlikely(level, "%s > %s: current score", resource1->id, resource2->id);
return -1;
}
/* 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) {
do_crm_log_unlikely(level, "%s < %s: colocated score NULL", resource1->id, resource2->id);
rc = 1;
break;
} else if(node2 == NULL) {
do_crm_log_unlikely(level, "%s > %s: colocated score NULL", resource1->id, resource2->id);
rc = -1;
break;
}
if(node1->weight < node2->weight) {
do_crm_log_unlikely(level, "%s < %s: colocated score", resource1->id, resource2->id);
rc = 1;
break;
} else if(node1->weight > node2->weight) {
do_crm_log_unlikely(level, "%s > %s: colocated score", resource1->id, resource2->id);
rc = -1;
break;
}
}
/* Order by reverse uname - same as sort_node_weight() does? */
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);
do_crm_log_unlikely(level, "%s %c %s: default", resource1->id, rc<0?'<':'>', resource2->id);
return rc;
}
static node_t *
can_run_instance(resource_t *rsc, node_t *node)
{
node_t *local_node = NULL;
clone_variant_data_t *clone_data = 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);
get_clone_variant_data(clone_data, rsc->parent);
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->count < clone_data->clone_node_max) {
crm_trace("%s can run on %s: %d",
rsc->id, node->details->uname, local_node->count);
return local_node;
} else {
crm_debug_2("%s cannot run on %s: node full (%d >= %d)",
rsc->id, node->details->uname, local_node->count, clone_data->clone_node_max);
}
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, pe_working_set_t *data_set)
{
node_t *chosen = NULL;
node_t *local_node = NULL;
crm_debug_2("Processing %s", rsc->id);
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)) {
crm_debug("Dependency loop detected involving %s", rsc->id);
return NULL;
}
/* Only include positive colocation preferences of dependant 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) {
crm_trace("Not pre-allocating %s to %s - unavailable", rsc->id, prefer->details->uname);
return NULL;
}
}
if(rsc->allowed_nodes) {
GHashTableIter iter;
node_t *try_node = NULL;
g_hash_table_iter_init (&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next (&iter, NULL, (void**)&try_node)) {
can_run_instance(rsc, try_node);
}
}
chosen = rsc->cmds->allocate(rsc, prefer, data_set);
if(chosen) {
local_node = pe_hash_table_lookup(
rsc->parent->allowed_nodes, chosen->details->id);
if(prefer && chosen && chosen->details != prefer->details) {
crm_err("Pre-allocation failed: got %s instead of %s",
chosen->details->uname, prefer->details->uname);
native_deallocate(rsc);
chosen = 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));
}
}
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);
}
}
}
node_t *
clone_color(resource_t *rsc, node_t *prefer, pe_working_set_t *data_set)
{
int allocated = 0;
GHashTableIter iter;
GListPtr gIter = NULL;
node_t *node = NULL;
int available_nodes = 0;
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)) {
crm_debug("Dependency loop detected involving %s", rsc->id);
return NULL;
}
set_bit(rsc->flags, pe_rsc_allocating);
crm_debug_2("Processing %s", rsc->id);
/* this information is used by sort_clone_instance() when deciding in which
* order to allocate clone instances
*/
gIter = rsc->rsc_cons;
for(; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data;
crm_trace("%s: Coloring %s first", rsc->id, constraint->rsc_rh->id);
constraint->rsc_rh->cmds->allocate(constraint->rsc_rh, prefer, data_set);
}
gIter = rsc->rsc_cons_lhs;
for(; 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, constraint->score/INFINITY, TRUE, TRUE);
}
dump_node_scores(show_scores?0:scores_log_level, rsc, __FUNCTION__, rsc->allowed_nodes);
/* count now tracks the number of clones currently allocated */
g_hash_table_iter_init (&iter, rsc->allowed_nodes);
while (g_hash_table_iter_next (&iter, NULL, (void**)&node)) {
node->count = 0;
if(can_run_resources(node)) {
available_nodes++;
}
}
rsc->children = g_list_sort_with_data(rsc->children, sort_clone_instance, data_set);
/* Pre-allocate as many instances as we can to their current location
*/
g_hash_table_iter_init (&iter, rsc->allowed_nodes);
while (available_nodes
&& available_nodes <= clone_data->clone_max
&& g_hash_table_iter_next (&iter, NULL, (void**)&node)) {
int lpc;
int loop_max = clone_data->clone_max / available_nodes;
if(loop_max < 1) {
loop_max = 1;
}
if(can_run_resources(node) == FALSE || node->weight < 0) {
crm_trace("Not Pre-allocatiing %s", node->details->uname);
continue;
}
crm_trace("Pre-allocatiing %s", node->details->uname);
for(lpc = 0;
allocated < clone_data->clone_max
&& node->count < clone_data->clone_node_max
&& lpc < clone_data->clone_node_max
&& lpc < loop_max;
lpc++) {
for(gIter = rsc->children; gIter != NULL; 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;
if(child_node->details == node->details
&& color_instance(child, node, clone_data->clone_max < available_nodes, data_set)) {
crm_trace("Pre-allocated %s to %s", child->id, node->details->uname);
allocated++;
break;
}
}
}
}
}
crm_trace("Done pre-allocating");
gIter = rsc->children;
for(; 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 >= clone_data->clone_max) {
crm_debug("Child %s not allocated - limit reached", child->id);
resource_location(child, NULL, -INFINITY, "clone_color:limit_reached", data_set);
} else if(color_instance(child, NULL, clone_data->clone_max < available_nodes, data_set)) {
allocated++;
}
}
crm_debug("Allocated %d %s instances of a possible %d",
allocated, rsc->id, clone_data->clone_max);
clear_bit(rsc->flags, pe_rsc_provisional);
clear_bit(rsc->flags, pe_rsc_allocating);
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)) {
crm_debug_3("Skipping optional: %s", action->uuid);
continue;
} else if(is_set(action->flags, pe_action_pseudo) == FALSE && is_set(action->flags, pe_action_runnable) == FALSE){
crm_debug_3("Skipping unrunnable: %s", action->uuid);
continue;
} else if(safe_str_eq(RSC_STOP, action->task)) {
crm_debug_2("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) {
crm_debug_3("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 {
crm_debug_2("Starting due to: %s", action->uuid);
crm_debug_3("%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 = rsc->children;
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;
}
for(; 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);
crm_free(key);
key = start_key(child);
start = find_rsc_action(child, key, active_only, NULL);
crm_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;
action_t *stop = NULL;
action_t *stopped = NULL;
action_t *start = NULL;
action_t *started = NULL;
GListPtr gIter = rsc->children;
resource_t *last_start_rsc = NULL;
resource_t *last_stop_rsc = NULL;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
crm_debug_2("Creating actions for %s", rsc->id);
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
child_rsc->cmds->create_actions(child_rsc, data_set);
clone_update_pseudo_status(
child_rsc, &child_stopping, &child_starting, &child_active);
if(is_set(child_rsc->flags, pe_rsc_starting)) {
last_start_rsc = child_rsc;
}
if(is_set(child_rsc->flags, pe_rsc_stopping)) {
last_stop_rsc = child_rsc;
}
}
/* 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);
update_action_flags(started, pe_action_pseudo);
started->priority = INFINITY;
if(child_active || child_starting) {
update_action_flags(started, pe_action_runnable);
}
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);
update_action_flags(stopped, pe_action_pseudo|pe_action_runnable);
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 = rsc->children;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
crm_trace("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);
}
for(; 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 void
assign_node(resource_t *rsc, node_t *node, gboolean force)
{
if(rsc->children) {
GListPtr gIter = rsc->children;
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
native_assign_node(child_rsc, NULL, node, force);
}
return;
}
native_assign_node(rsc, NULL, node, force);
}
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;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
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);
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) {
crm_debug_2("Pairing %s with %s on %s",
local_child->id, child_rsc->id, 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_debug_3("Can't pair %s with %s", local_child->id, rsc->id);
return NULL;
}
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;
clone_variant_data_t *clone_data = NULL;
get_clone_variant_data(clone_data, rsc);
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;
}
}
crm_debug("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(rsc_lh != NULL, return);
CRM_CHECK(rsc_lh->variant == pe_native, return);
get_clone_variant_data(clone_data, constraint->rsc_rh);
crm_debug_3("Processing constraint %s: %s -> %s %d",
constraint->id, rsc_lh->id, rsc_rh->id, constraint->score);
if(constraint->rsc_lh->variant >= pe_clone) {
get_clone_variant_data(clone_data_lh, constraint->rsc_lh);
if(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(rsc_rh == NULL) {
pe_err("rsc_rh was NULL for %s", constraint->id);
return;
} else if(is_set(rsc_rh->flags, pe_rsc_provisional)) {
crm_debug_3("%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) {
crm_debug("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 {
crm_debug("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) {
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 = crm_strdup(key+lpc);
task_mutable[stop-lpc] = 0;
crm_trace("Extracted action '%s' from '%s'", task_mutable, key);
- result = text2task(task_mutable);
+ result = get_complex_task(child, task_mutable, TRUE);
crm_free(task_mutable);
break;
}
}
} else {
- result = text2task(action->task);
+ result = get_complex_task(child, action->task, TRUE);
}
-
- switch(result) {
- case stopped_rsc:
- case started_rsc:
- case action_demoted:
- case action_promoted:
- result--;
- break;
- default:
- break;
- }
-
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);
crm_trace("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) {
crm_trace("%s is manditory because of %s", action->uuid, child_action->uuid);
clear_bit_inplace(flags, pe_action_optional);
clear_bit_inplace(action->flags, 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;
crm_trace("%s on %s (%s)", op->uuid, op->node?op->node->details->uname:"none", op->task);
}
}
}
if(check_runnable && any_runnable == FALSE) {
crm_trace("%s is not runnable because no children are", action->uuid);
clear_bit_inplace(flags, pe_action_runnable);
if(node == NULL) {
clear_bit_inplace(action->flags, 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(strstr(first->uuid, "_stopped_0") || strstr(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 */) {
crm_info("Inhibiting %s from being active", then_child->id);
assign_node(then_child, NULL, TRUE);
}
} else {
action_t *first_action = NULL;
action_t *then_action = NULL;
crm_debug("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);
CRM_CHECK(first_action != NULL || is_set(first_child->flags, pe_rsc_orphan),
crm_err("No action found for %s in %s (first)", first_task, first_child->id));
if(then_action == NULL
&& is_not_set(then_child->flags, pe_rsc_orphan)
&& crm_str_eq(then->task, RSC_STOP, TRUE) == FALSE
&& crm_str_eq(then->task, RSC_DEMOTED, TRUE) == FALSE) {
crm_err("Internal error: No action found for %s in %s (then)", then->task, then_child->id);
}
if(first_action == NULL || then_action == NULL) {
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, then_child->cmds->action_flags(then_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
&& first->rsc && first->rsc->variant >= pe_clone
&& then->rsc && then->rsc->variant >= pe_clone) {
clone_variant_data_t *clone_data = NULL;
if(strstr(then->uuid, "_stop_0") || strstr(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 {
GListPtr gIter = then->rsc->children;
changed |= native_update_actions(first, then, node, flags, filter, type);
for(; gIter != NULL; gIter = gIter->next) {
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)) {
changed |= child->cmds->update_actions(first, child_action, node, flags, filter, type);
}
}
}
}
return changed;
}
void clone_rsc_location(resource_t *rsc, rsc_to_node_t *constraint)
{
GListPtr gIter = rsc->children;
crm_debug_3("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);
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);
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);
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);
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;
}
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);
}
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;
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(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) {
resource_t *child_rsc = (resource_t*)gIter->data;
node_t *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 */
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");
crm_free(name);
name = crm_meta_name(XML_RSC_ATTR_NOTIFY);
crm_xml_add(xml, name, is_set(rsc->flags, pe_rsc_notify)?"true":"false");
crm_free(name);
name = crm_meta_name(XML_RSC_ATTR_INCARNATION_MAX);
crm_xml_add_int(xml, name, clone_data->clone_max);
crm_free(name);
name = crm_meta_name(XML_RSC_ATTR_INCARNATION_NODEMAX);
crm_xml_add_int(xml, name, clone_data->clone_node_max);
crm_free(name);
}
diff --git a/pengine/graph.c b/pengine/graph.c
index 22b8f551c2..d70a829216 100644
--- a/pengine/graph.c
+++ b/pengine/graph.c
@@ -1,670 +1,680 @@
/*
* 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 <sys/param.h>
#include <crm/crm.h>
#include <crm/cib.h>
#include <crm/msg_xml.h>
#include <crm/common/xml.h>
#include <crm/common/msg.h>
#include <glib.h>
#include <allocate.h>
#include <lib/pengine/utils.h>
#include <utils.h>
gboolean update_action(action_t *action);
gboolean rsc_update_action(action_t *first, action_t *then, enum pe_ordering type);
static action_t *rsc_expand_action(action_t *action)
{
action_t *result = action;
if(action->rsc && action->rsc->variant >= pe_group) {
- /* Expand 'first' */
+ /* Expand 'start' -> 'started' */
char *uuid = NULL;
gboolean notify = FALSE;
if(action->rsc->parent == NULL) {
/* Only outter-most resources have notification actions */
notify = is_set(action->rsc->flags, pe_rsc_notify);
}
uuid = convert_non_atomic_uuid(action->uuid, action->rsc, notify, FALSE);
- crm_trace("Converted %s to %s %d", action->uuid, uuid, is_set(action->rsc->flags, pe_rsc_notify));
- result = find_first_action(action->rsc->actions, uuid, NULL, NULL);
- CRM_CHECK(result != NULL, crm_err("Couldn't expand %s", action->uuid); result = action);
- crm_free(uuid);
+ if(uuid) {
+ crm_trace("Converting %s to %s %d", action->uuid, uuid, is_set(action->rsc->flags, pe_rsc_notify));
+ result = find_first_action(action->rsc->actions, uuid, NULL, NULL);
+ if(result == NULL) {
+ crm_err("Couldn't expand %s", action->uuid);
+ result = action;
+ }
+ crm_free(uuid);
+ }
}
return result;
}
static enum pe_graph_flags graph_update_action(action_t *first, action_t *then, node_t *node, enum pe_action_flags flags, enum pe_ordering type)
{
enum pe_graph_flags changed = pe_graph_none;
gboolean processed = FALSE;
/* TODO: Do as many of these in parallel as possible */
if(type & pe_order_implies_then) {
crm_trace("implies right: %s then %s", first->uuid, then->uuid);
processed = TRUE;
if(then->rsc) {
changed |= then->rsc->cmds->update_actions(
first, then, node, flags & pe_action_optional, pe_action_optional, pe_order_implies_then);
} else if(is_set(flags, pe_action_optional) == FALSE) {
if(update_action_flags(then, pe_action_optional|pe_action_clear)) {
changed |= pe_graph_updated_then;
}
}
}
if(type & pe_order_restart) {
enum pe_action_flags restart = (pe_action_optional|pe_action_runnable);
crm_trace("restart: %s then %s", first->uuid, then->uuid);
processed = TRUE;
changed |= then->rsc->cmds->update_actions(
first, then, node, flags & restart, restart, pe_order_restart);
}
if(type & pe_order_implies_first) {
crm_trace("implies left: %s then %s", first->uuid, then->uuid);
processed = TRUE;
if(first->rsc) {
changed |= first->rsc->cmds->update_actions(
first, then, node, flags & pe_action_optional, pe_action_optional, pe_order_implies_first);
} else if(is_set(flags, pe_action_optional) == FALSE) {
if(update_action_flags(first, pe_action_runnable|pe_action_clear)) {
changed |= pe_graph_updated_first;
}
}
}
if(type & pe_order_runnable_left) {
crm_trace("runnable: %s then %s", first->uuid, then->uuid);
processed = TRUE;
if(then->rsc) {
changed |= then->rsc->cmds->update_actions(
first, then, node, flags & pe_action_runnable, pe_action_runnable, pe_order_runnable_left);
} else if(is_set(flags, pe_action_runnable) == FALSE) {
if(update_action_flags(then, pe_action_runnable|pe_action_clear)) {
changed |= pe_graph_updated_then;
}
}
}
if(type & pe_order_optional) {
crm_trace("optional: %s then %s", first->uuid, then->uuid);
processed = TRUE;
if(then->rsc) {
changed |= then->rsc->cmds->update_actions(
first, then, node, flags & pe_action_runnable, pe_action_runnable, pe_order_optional);
}
}
if((type & pe_order_implies_then_printed) && (flags & pe_action_optional) == 0) {
processed = TRUE;
crm_trace("%s implies %s printed", first->uuid, then->uuid);
update_action_flags(then, pe_action_print_always); /* dont care about changed */
}
if((type & pe_order_implies_first_printed) && (flags & pe_action_optional) == 0) {
processed = TRUE;
crm_trace("%s implies %s printed", then->uuid, first->uuid);
update_action_flags(first, pe_action_print_always); /* dont care about changed */
}
if(processed == FALSE) {
crm_trace("Constraint 0x%.6x not applicable", type);
}
return changed;
}
gboolean
update_action(action_t *then)
{
GListPtr lpc = NULL;
enum pe_graph_flags changed = pe_graph_none;
crm_trace("Processing %s (%s %s %s)",
then->uuid,
is_set(then->flags, pe_action_optional)?"optional":"required",
is_set(then->flags, pe_action_runnable)?"runnable":"unrunnable",
is_set(then->flags, pe_action_pseudo)?"pseudo":then->node?then->node->details->uname:"");
for(lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
action_wrapper_t *other = (action_wrapper_t*)lpc->data;
action_t *first = other->action;
node_t *then_node = then->node;
node_t *first_node = first->node;
enum pe_action_flags then_flags = 0;
enum pe_action_flags first_flags = 0;
if(first->rsc
&& first->rsc->variant == pe_group
&& safe_str_eq(first->task, RSC_START)) {
first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
if(first_node) {
crm_trace("First: Found node %s for %s", first_node->details->uname, first->uuid);
}
}
if(then->rsc
&& then->rsc->variant == pe_group
&& safe_str_eq(then->task, RSC_START)) {
then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
if(then_node) {
crm_trace("Then: Found node %s for %s", then_node->details->uname, then->uuid);
}
}
+ clear_bit_inplace(changed, pe_graph_updated_first);
+
+ if(first->rsc != then->rsc
+ && first->rsc != NULL
+ && then->rsc != NULL
+ && first->rsc != then->rsc->parent) {
+ first = rsc_expand_action(first);
+ }
+ if(first != other->action) {
+ crm_trace("Ordering %s afer %s instead of %s", then->uuid, first->uuid, other->action->uuid);
+ }
+
first_flags = get_action_flags(first, then_node);
then_flags = get_action_flags(then, first_node);
-
+
crm_trace("Checking %s (%s %s %s) against %s (%s %s %s) 0x%.6x",
then->uuid,
is_set(then_flags, pe_action_optional)?"optional":"required",
is_set(then_flags, pe_action_runnable)?"runnable":"unrunnable",
is_set(then_flags, pe_action_pseudo)?"pseudo":then->node?then->node->details->uname:"",
first->uuid,
is_set(first_flags, pe_action_optional)?"optional":"required",
is_set(first_flags, pe_action_runnable)?"runnable":"unrunnable",
is_set(first_flags, pe_action_pseudo)?"pseudo":first->node?first->node->details->uname:"",
other->type);
- clear_bit_inplace(changed, pe_graph_updated_first);
- if(first->rsc != then->rsc
- && (then->rsc == NULL || first->rsc != then->rsc->parent)) {
- first = rsc_expand_action(first);
- }
-
if(first == other->action) {
clear_bit_inplace(first_flags, pe_action_pseudo);
changed |= graph_update_action(first, then, then->node, first_flags, other->type);
} else if(order_actions(first, then, other->type)) {
- crm_trace("Ordering %s afer %s instead of %s", then->uuid, first->uuid, other->action->uuid);
/* Start again to get the new actions_before list */
changed |= (pe_graph_updated_then|pe_graph_disable);
}
if(changed & pe_graph_disable) {
- crm_trace("Disabled constraint %s -> %s", then->uuid, first->uuid);
+ crm_trace("Disabled constraint %s -> %s", other->action->uuid, then->uuid);
clear_bit_inplace(changed, pe_graph_disable);
other->type = pe_order_none;
}
if(changed & pe_graph_updated_first) {
GListPtr lpc2 = NULL;
crm_trace("Updated %s (first %s %s %s), processing dependants ",
first->uuid,
is_set(first->flags, pe_action_optional)?"optional":"required",
is_set(first->flags, pe_action_runnable)?"runnable":"unrunnable",
is_set(first->flags, pe_action_pseudo)?"pseudo":first->node?first->node->details->uname:"");
for(lpc2 = first->actions_after; lpc2 != NULL; lpc2 = lpc2->next) {
action_wrapper_t *other = (action_wrapper_t*)lpc2->data;
update_action(other->action);
}
update_action(first);
}
}
if(changed & pe_graph_updated_then) {
crm_trace("Updated %s (then %s %s %s), processing dependants ",
then->uuid,
is_set(then->flags, pe_action_optional)?"optional":"required",
is_set(then->flags, pe_action_runnable)?"runnable":"unrunnable",
is_set(then->flags, pe_action_pseudo)?"pseudo":then->node?then->node->details->uname:"");
update_action(then);
for(lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
action_wrapper_t *other = (action_wrapper_t*)lpc->data;
update_action(other->action);
}
}
return FALSE;
}
gboolean
shutdown_constraints(
node_t *node, action_t *shutdown_op, pe_working_set_t *data_set)
{
/* add the stop to the before lists so it counts as a pre-req
* for the shutdown
*/
GListPtr lpc = NULL;
for(lpc = node->details->running_rsc; lpc != NULL; lpc = lpc->next) {
resource_t *rsc = (resource_t*)lpc->data;
if(is_not_set(rsc->flags, pe_rsc_managed)) {
continue;
}
custom_action_order(
rsc, stop_key(rsc), NULL,
NULL, crm_strdup(CRM_OP_SHUTDOWN), shutdown_op,
pe_order_optional, data_set);
}
return TRUE;
}
gboolean
stonith_constraints(
node_t *node, action_t *stonith_op, pe_working_set_t *data_set)
{
CRM_CHECK(stonith_op != NULL, return FALSE);
/*
* Make sure the stonith OP occurs before we start any shared resources
*/
if(stonith_op != NULL) {
GListPtr lpc = NULL;
for(lpc = data_set->resources; lpc != NULL; lpc = lpc->next) {
resource_t *rsc = (resource_t*)lpc->data;
rsc_stonith_ordering(rsc, stonith_op, data_set);
}
}
/* add the stonith OP as a stop pre-req and the mark the stop
* as a pseudo op - since its now redundant
*/
return TRUE;
}
xmlNode *
action2xml(action_t *action, gboolean as_input)
{
gboolean needs_node_info = TRUE;
xmlNode * action_xml = NULL;
xmlNode * args_xml = NULL;
char *action_id_s = NULL;
if(action == NULL) {
return NULL;
}
crm_debug_4("Dumping action %d as XML", action->id);
if(safe_str_eq(action->task, CRM_OP_FENCE)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
/* needs_node_info = FALSE; */
} else if(safe_str_eq(action->task, CRM_OP_SHUTDOWN)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
} else if(safe_str_eq(action->task, CRM_OP_CLEAR_FAILCOUNT)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
} else if(safe_str_eq(action->task, CRM_OP_LRM_REFRESH)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT);
/* } else if(safe_str_eq(action->task, RSC_PROBED)) { */
/* action_xml = create_xml_node(NULL, XML_GRAPH_TAG_CRM_EVENT); */
} else if(is_set(action->flags, pe_action_pseudo)) {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_PSEUDO_EVENT);
needs_node_info = FALSE;
} else {
action_xml = create_xml_node(NULL, XML_GRAPH_TAG_RSC_OP);
}
action_id_s = crm_itoa(action->id);
crm_xml_add(action_xml, XML_ATTR_ID, action_id_s);
crm_free(action_id_s);
crm_xml_add(action_xml, XML_LRM_ATTR_TASK, action->task);
if(action->rsc != NULL && action->rsc->clone_name != NULL) {
char *clone_key = NULL;
const char *interval_s = g_hash_table_lookup(action->meta, "interval");
int interval = crm_parse_int(interval_s, "0");
if(safe_str_eq(action->task, RSC_NOTIFY)) {
const char *n_type = g_hash_table_lookup(action->meta, "notify_type");
const char *n_task = g_hash_table_lookup(action->meta, "notify_operation");
CRM_CHECK(n_type != NULL, crm_err("No notify type value found for %s", action->uuid));
CRM_CHECK(n_task != NULL, crm_err("No notify operation value found for %s", action->uuid));
clone_key = generate_notify_key(action->rsc->clone_name, n_type, n_task);
} else {
clone_key = generate_op_key(action->rsc->clone_name, action->task, interval);
}
CRM_CHECK(clone_key != NULL, crm_err("Could not generate a key for %s", action->uuid));
crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, clone_key);
crm_xml_add(action_xml, "internal_"XML_LRM_ATTR_TASK_KEY, action->uuid);
crm_free(clone_key);
} else {
crm_xml_add(action_xml, XML_LRM_ATTR_TASK_KEY, action->uuid);
}
if(needs_node_info && action->node != NULL) {
crm_xml_add(action_xml, XML_LRM_ATTR_TARGET,
action->node->details->uname);
crm_xml_add(action_xml, XML_LRM_ATTR_TARGET_UUID,
action->node->details->id);
}
if(is_set(action->flags, pe_action_failure_is_fatal) == FALSE) {
add_hash_param(action->meta,
XML_ATTR_TE_ALLOWFAIL, XML_BOOLEAN_TRUE);
}
if(as_input) {
return action_xml;
}
if(action->rsc) {
if(is_set(action->flags, pe_action_pseudo) == FALSE) {
int lpc = 0;
xmlNode *rsc_xml = create_xml_node(
action_xml, crm_element_name(action->rsc->xml));
const char *attr_list[] = {
XML_AGENT_ATTR_CLASS,
XML_AGENT_ATTR_PROVIDER,
XML_ATTR_TYPE
};
if(action->rsc->clone_name != NULL) {
crm_debug("Using clone name %s for %s", action->rsc->clone_name, action->rsc->id);
crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->clone_name);
crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->id);
} else {
crm_xml_add(rsc_xml, XML_ATTR_ID, action->rsc->id);
crm_xml_add(rsc_xml, XML_ATTR_ID_LONG, action->rsc->long_name);
}
for(lpc = 0; lpc < DIMOF(attr_list); lpc++) {
crm_xml_add(rsc_xml, attr_list[lpc],
g_hash_table_lookup(action->rsc->meta, attr_list[lpc]));
}
}
}
args_xml = create_xml_node(NULL, XML_TAG_ATTRS);
crm_xml_add(args_xml, XML_ATTR_CRM_VERSION, CRM_FEATURE_SET);
g_hash_table_foreach(action->extra, hash2field, args_xml);
if(action->rsc != NULL) {
g_hash_table_foreach(action->rsc->parameters, hash2smartfield, args_xml);
}
g_hash_table_foreach(action->meta, hash2metafield, args_xml);
if(action->rsc != NULL) {
resource_t *parent = action->rsc;
while(parent != NULL) {
parent->cmds->append_meta(parent, args_xml);
parent = parent->parent;
}
} else if(safe_str_eq(action->task, CRM_OP_FENCE)) {
g_hash_table_foreach(action->node->details->attrs, hash2metafield, args_xml);
}
sorted_xml(args_xml, action_xml, FALSE);
crm_log_xml_debug_4(action_xml, "dumped action");
free_xml(args_xml);
return action_xml;
}
static gboolean
should_dump_action(action_t *action)
{
int log_filter = LOG_DEBUG_5;
CRM_CHECK(action != NULL, return FALSE);
if(is_set(action->flags, pe_action_dumped)) {
do_crm_log_unlikely(log_filter, "action %d (%s) was already dumped",
action->id, action->uuid);
return FALSE;
} else if(is_set(action->flags, pe_action_runnable) == FALSE) {
do_crm_log_unlikely(log_filter, "action %d (%s) was not runnable",
action->id, action->uuid);
return FALSE;
} else if(is_set(action->flags, pe_action_optional) && is_set(action->flags, pe_action_print_always) == FALSE) {
do_crm_log_unlikely(log_filter, "action %d (%s) was optional",
action->id, action->uuid);
return FALSE;
} else if(action->rsc != NULL
&& is_not_set(action->rsc->flags, pe_rsc_managed)) {
const char * interval = NULL;
interval = g_hash_table_lookup(action->meta, XML_LRM_ATTR_INTERVAL);
/* make sure probes and recurring monitors go through */
if(safe_str_neq(action->task, RSC_STATUS) && interval == NULL) {
do_crm_log_unlikely(log_filter, "action %d (%s) was for an unmanaged resource (%s)",
action->id, action->uuid, action->rsc->id);
return FALSE;
}
}
if(is_set(action->flags, pe_action_pseudo)
|| safe_str_eq(action->task, CRM_OP_FENCE)
|| safe_str_eq(action->task, CRM_OP_SHUTDOWN)) {
/* skip the next checks */
return TRUE;
}
if(action->node == NULL) {
pe_err("action %d (%s) was not allocated",
action->id, action->uuid);
log_action(LOG_DEBUG, "Unallocated action", action, FALSE);
return FALSE;
} else if(action->node->details->online == FALSE) {
pe_err("action %d was (%s) scheduled for offline node",
action->id, action->uuid);
log_action(LOG_DEBUG, "Action for offline node", action, FALSE);
return FALSE;
#if 0
/* but this would also affect resources that can be safely
* migrated before a fencing op
*/
} else if(action->node->details->unclean == FALSE) {
pe_err("action %d was (%s) scheduled for unclean node",
action->id, action->uuid);
log_action(LOG_DEBUG, "Action for unclean node", action, FALSE);
return FALSE;
#endif
}
return TRUE;
}
/* lowest to highest */
static gint sort_action_id(gconstpointer a, gconstpointer b)
{
const action_wrapper_t *action_wrapper2 = (const action_wrapper_t*)a;
const action_wrapper_t *action_wrapper1 = (const action_wrapper_t*)b;
if(a == NULL) { return 1; }
if(b == NULL) { return -1; }
if(action_wrapper1->action->id > action_wrapper2->action->id) {
return -1;
}
if(action_wrapper1->action->id < action_wrapper2->action->id) {
return 1;
}
return 0;
}
static gboolean
should_dump_input(int last_action, action_t *action, action_wrapper_t *wrapper)
{
int type = wrapper->type;
int log_dump = LOG_DEBUG_3;
int log_filter = LOG_DEBUG_3;
type &= ~pe_order_implies_first_printed;
type &= ~pe_order_implies_then_printed;
type &= ~pe_order_optional;
wrapper->state = pe_link_not_dumped;
if(last_action == wrapper->action->id) {
do_crm_log_unlikely(log_filter, "Input (%d) %s duplicated for %s",
wrapper->action->id, wrapper->action->uuid, action->uuid);
wrapper->state = pe_link_dup;
return FALSE;
} else if(wrapper->type == pe_order_none) {
do_crm_log_unlikely(log_filter, "Input (%d) %s suppressed for %s",
wrapper->action->id, wrapper->action->uuid, action->uuid);
return FALSE;
} else if(is_set(wrapper->action->flags, pe_action_runnable) == FALSE
&& type == pe_order_none
&& safe_str_neq(wrapper->action->uuid, CRM_OP_PROBED)) {
do_crm_log_unlikely(log_filter, "Input (%d) %s optional (ordering) for %s",
wrapper->action->id, wrapper->action->uuid, action->uuid);
return FALSE;
} else if(is_set(action->flags, pe_action_pseudo)
&& (wrapper->type & pe_order_stonith_stop)) {
do_crm_log_unlikely(log_filter, "Input (%d) %s suppressed for %s",
wrapper->action->id, wrapper->action->uuid, action->uuid);
return FALSE;
} else if(wrapper->action->rsc
&& wrapper->action->rsc != action->rsc
&& is_set(wrapper->action->rsc->flags, pe_rsc_failed)
&& is_not_set(wrapper->action->rsc->flags, pe_rsc_managed)
&& strstr(wrapper->action->uuid, "_stop_0")
&& action->rsc && action->rsc->variant >= pe_clone) {
crm_warn("Ignoring requirement that %s comeplete before %s:"
" unmanaged failed resources cannot prevent clone shutdown",
wrapper->action->uuid, action->uuid);
return FALSE;
} else if(is_set(wrapper->action->flags, pe_action_dumped) || should_dump_action(wrapper->action)) {
do_crm_log_unlikely(log_dump, "Input (%d) %s should be dumped for %s",
wrapper->action->id, wrapper->action->uuid, action->uuid);
goto dump;
#if 0
} else if(is_set(wrapper->action->flags, pe_action_runnable)
&& is_set(wrapper->action->flags, pe_action_pseudo)
&& wrapper->action->rsc->variant != pe_native) {
do_crm_log(LOG_CRIT, "Input (%d) %s should be dumped for %s",
wrapper->action->id, wrapper->action->uuid, action->uuid);
goto dump;
#endif
} else if(is_set(wrapper->action->flags, pe_action_optional) == TRUE && is_set(wrapper->action->flags, pe_action_print_always) == FALSE) {
do_crm_log_unlikely(log_filter, "Input (%d) %s optional for %s",
wrapper->action->id, wrapper->action->uuid, action->uuid);
do_crm_log_unlikely(log_filter, "Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x",
wrapper->action->id,
wrapper->action->uuid,
wrapper->action->node,
is_set(wrapper->action->flags, pe_action_pseudo),
is_set(wrapper->action->flags, pe_action_runnable),
is_set(wrapper->action->flags, pe_action_optional),
is_set(wrapper->action->flags, pe_action_print_always),
wrapper->type);
return FALSE;
}
dump:
do_crm_log_unlikely(log_dump, "Input (%d) %s n=%p p=%d r=%d o=%d a=%d f=0x%.6x dumped for %s",
wrapper->action->id,
wrapper->action->uuid,
wrapper->action->node,
is_set(wrapper->action->flags, pe_action_pseudo),
is_set(wrapper->action->flags, pe_action_runnable),
is_set(wrapper->action->flags, pe_action_optional),
is_set(wrapper->action->flags, pe_action_print_always),
wrapper->type, action->uuid);
return TRUE;
}
void
graph_element_from_action(action_t *action, pe_working_set_t *data_set)
{
GListPtr lpc = NULL;
int last_action = -1;
int synapse_priority = 0;
xmlNode * syn = NULL;
xmlNode * set = NULL;
xmlNode * in = NULL;
xmlNode * input = NULL;
xmlNode * xml_action = NULL;
if(should_dump_action(action) == FALSE) {
return;
}
set_bit_inplace(action->flags, pe_action_dumped);
syn = create_xml_node(data_set->graph, "synapse");
set = create_xml_node(syn, "action_set");
in = create_xml_node(syn, "inputs");
crm_xml_add_int(syn, XML_ATTR_ID, data_set->num_synapse);
data_set->num_synapse++;
if(action->rsc != NULL) {
synapse_priority = action->rsc->priority;
}
if(action->priority > synapse_priority) {
synapse_priority = action->priority;
}
if(synapse_priority > 0) {
crm_xml_add_int(syn, XML_CIB_ATTR_PRIORITY, synapse_priority);
}
xml_action = action2xml(action, FALSE);
add_node_nocopy(set, crm_element_name(xml_action), xml_action);
action->actions_before = g_list_sort(
action->actions_before, sort_action_id);
for(lpc = action->actions_before; lpc != NULL; lpc = lpc->next) {
action_wrapper_t *wrapper = (action_wrapper_t*)lpc->data;
if(should_dump_input(last_action, action, wrapper) == FALSE) {
continue;
}
wrapper->state = pe_link_dumped;
CRM_CHECK(last_action < wrapper->action->id, ;);
last_action = wrapper->action->id;
input = create_xml_node(in, "trigger");
xml_action = action2xml(wrapper->action, TRUE);
add_node_nocopy(input, crm_element_name(xml_action), xml_action);
}
}
diff --git a/pengine/group.c b/pengine/group.c
index c9468267ec..077de5cfe3 100644
--- a/pengine/group.c
+++ b/pengine/group.c
@@ -1,487 +1,478 @@
/*
* 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 <pengine.h>
#include <lib/pengine/utils.h>
#include <crm/msg_xml.h>
#include <allocate.h>
#include <utils.h>
#define VARIANT_GROUP 1
#include <lib/pengine/variant.h>
node_t *
group_color(resource_t *rsc, node_t *prefer, pe_working_set_t *data_set)
{
node_t *node = NULL;
node_t *group_node = NULL;
GListPtr gIter = rsc->children;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
if(is_not_set(rsc->flags, pe_rsc_provisional)) {
return rsc->allocated_to;
}
crm_debug_2("Processing %s", rsc->id);
if(is_set(rsc->flags, pe_rsc_allocating)) {
crm_debug("Dependency loop detected involving %s", rsc->id);
return NULL;
}
if(group_data->first_child == NULL) {
/* nothign to allocate */
clear_bit(rsc->flags, pe_rsc_provisional);
return NULL;
}
set_bit(rsc->flags, pe_rsc_allocating);
rsc->role = group_data->first_child->role;
group_data->first_child->rsc_cons = g_list_concat(
group_data->first_child->rsc_cons, rsc->rsc_cons);
rsc->rsc_cons = NULL;
group_data->first_child->rsc_cons_lhs = g_list_concat(
group_data->first_child->rsc_cons_lhs, rsc->rsc_cons_lhs);
rsc->rsc_cons_lhs = NULL;
dump_node_scores(show_scores?0:scores_log_level, rsc, __PRETTY_FUNCTION__, rsc->allowed_nodes);
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
node = child_rsc->cmds->allocate(child_rsc, prefer, data_set);
if(group_node == NULL) {
group_node = node;
}
}
rsc->next_role = group_data->first_child->next_role;
clear_bit(rsc->flags, pe_rsc_allocating);
clear_bit(rsc->flags, pe_rsc_provisional);
if(group_data->colocated) {
return group_node;
}
return NULL;
}
void group_update_pseudo_status(resource_t *parent, resource_t *child);
void group_create_actions(resource_t *rsc, pe_working_set_t *data_set)
{
action_t *op = NULL;
const char *value = NULL;
GListPtr gIter = rsc->children;
crm_debug_2("Creating actions for %s", rsc->id);
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
child_rsc->cmds->create_actions(child_rsc, data_set);
group_update_pseudo_status(rsc, child_rsc);
}
op = start_action(rsc, NULL, TRUE/* !group_data->child_starting */);
set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable);
op = custom_action(rsc, started_key(rsc),
RSC_STARTED, NULL,
TRUE/* !group_data->child_starting */, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable);
op = stop_action(rsc, NULL, TRUE/* !group_data->child_stopping */);
set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable);
op = custom_action(rsc, stopped_key(rsc),
RSC_STOPPED, NULL,
TRUE/* !group_data->child_stopping */, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo|pe_action_runnable);
value = g_hash_table_lookup(rsc->meta, "stateful");
if(crm_is_true(value)) {
op = custom_action(rsc, demote_key(rsc), RSC_DEMOTE, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable);
op = custom_action(rsc, demoted_key(rsc), RSC_DEMOTED, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable);
op = custom_action(rsc, promote_key(rsc), RSC_PROMOTE, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable);
op = custom_action(rsc, promoted_key(rsc), RSC_PROMOTED, NULL, TRUE, TRUE, data_set);
set_bit_inplace(op->flags, pe_action_pseudo); set_bit_inplace(op->flags, pe_action_runnable);
}
}
void
group_update_pseudo_status(resource_t *parent, resource_t *child)
{
GListPtr gIter = child->actions;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, parent);
if(group_data->ordered == FALSE) {
/* If this group is not ordered, then leave the meta-actions as optional */
return;
}
if(group_data->child_stopping && group_data->child_starting) {
return;
}
for(; gIter != NULL; gIter = gIter->next) {
action_t *action = (action_t*)gIter->data;
if(is_set(action->flags, pe_action_optional)) {
continue;
}
if(safe_str_eq(RSC_STOP, action->task) && is_set(action->flags, pe_action_runnable)) {
group_data->child_stopping = TRUE;
crm_debug_3("Based on %s the group is stopping", action->uuid);
} else if(safe_str_eq(RSC_START, action->task) && is_set(action->flags, pe_action_runnable)) {
group_data->child_starting = TRUE;
crm_debug_3("Based on %s the group is starting", action->uuid);
}
}
}
void group_internal_constraints(resource_t *rsc, pe_working_set_t *data_set)
{
GListPtr gIter = rsc->children;
resource_t *last_rsc = NULL;
resource_t *top = uber_parent(rsc);
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
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);
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
int stop = pe_order_none;
int stopped = pe_order_implies_then_printed;
int start = pe_order_implies_then|pe_order_runnable_left;
int started = pe_order_runnable_left|pe_order_implies_then|pe_order_implies_then_printed;
child_rsc->cmds->internal_constraints(child_rsc, data_set);
if(last_rsc == NULL) {
if(group_data->ordered) {
stop |= pe_order_optional;
stopped = pe_order_implies_then;
}
} else if(group_data->colocated) {
rsc_colocation_new(
"group:internal_colocation", NULL, INFINITY,
child_rsc, last_rsc, NULL, NULL, data_set);
}
if(top->variant == pe_master) {
new_rsc_order(rsc, RSC_DEMOTE, child_rsc, RSC_DEMOTE,
stop|pe_order_implies_first_printed, data_set);
new_rsc_order(child_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stopped, data_set);
new_rsc_order(child_rsc, RSC_PROMOTE, rsc, RSC_PROMOTED, started, data_set);
new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE,
pe_order_implies_first_printed, data_set);
}
order_start_start(rsc, child_rsc, pe_order_implies_first_printed);
order_stop_stop(rsc, child_rsc, stop|pe_order_implies_first_printed);
new_rsc_order(child_rsc, RSC_STOP, rsc, RSC_STOPPED, stopped, data_set);
new_rsc_order(child_rsc, RSC_START, rsc, RSC_STARTED, started, data_set);
if(group_data->ordered == FALSE) {
order_start_start(rsc, child_rsc, start|pe_order_implies_first_printed);
if(top->variant == pe_master) {
new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE,
start|pe_order_implies_first_printed, data_set);
}
} else if(last_rsc != NULL) {
child_rsc->restart_type = pe_restart_restart;
order_start_start(last_rsc, child_rsc, start);
order_stop_stop(child_rsc, last_rsc, pe_order_optional);
if(top->variant == pe_master) {
new_rsc_order(last_rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, start, data_set);
new_rsc_order(child_rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, pe_order_optional, data_set);
}
} else {
/* If anyone in the group is starting, then
* pe_order_implies_then will cause _everyone_ in the group
* to be sent a start action
* But this is safe since starting something that is already
* started is required to be "safe"
*/
int flags = pe_order_none;
order_start_start(rsc, child_rsc, flags);
if(top->variant == pe_master) {
new_rsc_order(rsc, RSC_PROMOTE, child_rsc, RSC_PROMOTE, flags, data_set);
}
}
last_rsc = child_rsc;
}
if(group_data->ordered && last_rsc != NULL) {
int stop_stop_flags = pe_order_implies_then;
int stop_stopped_flags = pe_order_optional;
order_stop_stop(rsc, last_rsc, stop_stop_flags);
new_rsc_order(last_rsc, RSC_STOP, rsc, RSC_STOPPED, stop_stopped_flags, data_set);
if(top->variant == pe_master) {
new_rsc_order(rsc, RSC_DEMOTE, last_rsc, RSC_DEMOTE, stop_stop_flags, data_set);
new_rsc_order(last_rsc, RSC_DEMOTE, rsc, RSC_DEMOTED, stop_stopped_flags, data_set);
}
}
}
void group_rsc_colocation_lh(
resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint)
{
GListPtr gIter = NULL;
group_variant_data_t *group_data = NULL;
if(rsc_lh == NULL) {
pe_err("rsc_lh was NULL for %s", constraint->id);
return;
} else if(rsc_rh == NULL) {
pe_err("rsc_rh was NULL for %s", constraint->id);
return;
}
gIter = rsc_lh->children;
crm_debug_4("Processing constraints from %s", rsc_lh->id);
get_group_variant_data(group_data, rsc_lh);
if(group_data->colocated) {
group_data->first_child->cmds->rsc_colocation_lh(
group_data->first_child, rsc_rh, constraint);
return;
} else if(constraint->score >= INFINITY) {
crm_config_err("%s: Cannot perform manditory colocation"
" between non-colocated group and %s",
rsc_lh->id, rsc_rh->id);
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);
}
}
void group_rsc_colocation_rh(
resource_t *rsc_lh, resource_t *rsc_rh, rsc_colocation_t *constraint)
{
GListPtr gIter = rsc_rh->children;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc_rh);
CRM_CHECK(rsc_lh->variant == pe_native, return);
crm_debug_3("Processing RH of constraint %s", constraint->id);
print_resource(LOG_DEBUG_3, "LHS", rsc_lh, TRUE);
if(is_set(rsc_rh->flags, pe_rsc_provisional)) {
return;
} else if(group_data->colocated && group_data->first_child) {
if(constraint->score >= INFINITY) {
/* Ensure RHS is _fully_ up before can start LHS */
group_data->last_child->cmds->rsc_colocation_rh(
rsc_lh, group_data->last_child, constraint);
} else {
/* A partially active RHS is fine */
group_data->first_child->cmds->rsc_colocation_rh(
rsc_lh, group_data->first_child, constraint);
}
return;
} else if(constraint->score >= INFINITY) {
crm_config_err("%s: Cannot perform manditory colocation with"
" non-colocated group: %s", rsc_lh->id, rsc_rh->id);
return;
}
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);
}
}
enum pe_action_flags group_action_flags(action_t *action, node_t *node)
{
- gboolean check_runnable = FALSE;
- const char *task_s = action->task;
- GListPtr gIter = action->rsc->children;
- enum action_tasks task = text2task(task_s);
+ GListPtr gIter = NULL;
enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
- switch(task) {
- case stopped_rsc:
- case started_rsc:
- case action_demoted:
- case action_promoted:
- task_s = task2text(task-1);
- check_runnable = TRUE;
- break;
- default:
- break;
- }
-
- for(; gIter != NULL; gIter = gIter->next) {
+ for(gIter = action->rsc->children; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
+ enum action_tasks task = get_complex_task(child, action->task, TRUE);
+ const char *task_s = task2text(task);
action_t *child_action = find_first_action(child->actions, NULL, task_s, node);
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) {
crm_trace("%s is manditory because of %s", action->uuid, child_action->uuid);
clear_bit_inplace(flags, pe_action_optional);
clear_bit_inplace(action->flags, pe_action_optional);
}
- if(check_runnable
+ if(safe_str_neq(task_s, action->task)
&& is_set(flags, pe_action_runnable) && is_set(child_flags, pe_action_runnable) == FALSE) {
crm_trace("%s is not runnable because of %s", action->uuid, child_action->uuid);
clear_bit_inplace(flags, pe_action_runnable);
clear_bit_inplace(action->flags, pe_action_runnable);
}
+
+ } else if(task != stop_rsc) {
+ crm_trace("%s is not runnable because of %s (not found in %s)", action->uuid, task_s, child->id);
+ clear_bit_inplace(flags, pe_action_runnable);
}
}
return flags;
}
enum pe_graph_flags group_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)
{
GListPtr gIter = then->rsc->children;
enum pe_graph_flags changed = pe_graph_none;
CRM_ASSERT(then->rsc != NULL);
changed |= native_update_actions(first, then, node, flags, filter, type);
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child = (resource_t*)gIter->data;
action_t *child_action = find_first_action(child->actions, NULL, then->task, node);
if(child_action) {
changed |= child->cmds->update_actions(first, child_action, node, flags, filter, type);
}
}
return changed;
}
void group_rsc_location(resource_t *rsc, rsc_to_node_t *constraint)
{
GListPtr gIter = rsc->children;
GListPtr saved = constraint->node_list_rh;
GListPtr zero = node_list_dup(constraint->node_list_rh, TRUE, FALSE);
gboolean reset_scores = TRUE;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
crm_debug("Processing rsc_location %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);
if(group_data->colocated && reset_scores) {
reset_scores = FALSE;
constraint->node_list_rh = zero;
}
}
constraint->node_list_rh = saved;
slist_basic_destroy(zero);
}
void group_expand(resource_t *rsc, pe_working_set_t *data_set)
{
GListPtr gIter = rsc->children;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
crm_debug_3("Processing actions from %s", rsc->id);
CRM_CHECK(rsc != NULL, return);
native_expand(rsc, data_set);
for(; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t*)gIter->data;
child_rsc->cmds->expand(child_rsc, data_set);
}
}
GHashTable *
group_merge_weights(
resource_t *rsc, const char *rhs, GHashTable *nodes, const char *attr, int factor, gboolean allow_rollback, gboolean only_positive)
{
GListPtr gIter = rsc->rsc_cons_lhs;
group_variant_data_t *group_data = NULL;
get_group_variant_data(group_data, rsc);
if(is_set(rsc->flags, pe_rsc_merging)) {
crm_info("Breaking dependency loop with %s at %s", rsc->id, rhs);
return nodes;
}
set_bit(rsc->flags, pe_rsc_merging);
nodes = group_data->first_child->cmds->merge_weights(
group_data->first_child, rhs, nodes, attr, factor, allow_rollback, only_positive);
for(; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t*)gIter->data;
nodes = native_merge_weights(
constraint->rsc_lh, rsc->id, nodes,
constraint->node_attribute,
constraint->score/INFINITY, allow_rollback, only_positive);
}
clear_bit(rsc->flags, pe_rsc_merging);
return nodes;
}
void group_append_meta(resource_t *rsc, xmlNode *xml)
{
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 8, 5:51 PM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002385
Default Alt Text
(196 KB)
Attached To
Mode
rP Pacemaker
Attached
Detach File
Event Timeline
Log In to Comment