Page MenuHomeClusterLabs Projects

No OneTemporary

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

Mime Type
text/x-diff
Expires
Tue, Jul 8, 5:51 PM (1 d, 2 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
2002385
Default Alt Text
(196 KB)

Event Timeline