mirror of https://github.com/sipwise/heartbeat.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
558 lines
14 KiB
558 lines
14 KiB
/*
|
|
* 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.1 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include <lha_internal.h>
|
|
#include <crm/msg_xml.h>
|
|
#include <allocate.h>
|
|
#include <utils.h>
|
|
#include <lib/crm/pengine/utils.h>
|
|
|
|
void
|
|
print_rsc_to_node(const char *pre_text, rsc_to_node_t *cons, gboolean details)
|
|
{
|
|
if(cons == NULL) {
|
|
crm_debug_4("%s%s: <NULL>",
|
|
pre_text==NULL?"":pre_text,
|
|
pre_text==NULL?"":": ");
|
|
return;
|
|
}
|
|
crm_debug_4("%s%s%s Constraint %s (%p) - %d nodes:",
|
|
pre_text==NULL?"":pre_text,
|
|
pre_text==NULL?"":": ",
|
|
"rsc_to_node",
|
|
cons->id, cons,
|
|
g_list_length(cons->node_list_rh));
|
|
|
|
if(details == FALSE) {
|
|
crm_debug_4("\t%s (node placement rule)",
|
|
safe_val3(NULL, cons, rsc_lh, id));
|
|
|
|
slist_iter(
|
|
node, node_t, cons->node_list_rh, lpc,
|
|
print_node("\t\t-->", node, FALSE)
|
|
);
|
|
}
|
|
}
|
|
|
|
void
|
|
print_rsc_colocation(const char *pre_text, rsc_colocation_t *cons, gboolean details)
|
|
{
|
|
if(cons == NULL) {
|
|
crm_debug_4("%s%s: <NULL>",
|
|
pre_text==NULL?"":pre_text,
|
|
pre_text==NULL?"":": ");
|
|
return;
|
|
}
|
|
crm_debug_4("%s%s%s Constraint %s (%p):",
|
|
pre_text==NULL?"":pre_text,
|
|
pre_text==NULL?"":": ",
|
|
XML_CONS_TAG_RSC_DEPEND, cons->id, cons);
|
|
|
|
if(details == FALSE) {
|
|
|
|
crm_debug_4("\t%s --> %s, %d",
|
|
safe_val3(NULL, cons, rsc_lh, id),
|
|
safe_val3(NULL, cons, rsc_rh, id),
|
|
cons->score);
|
|
}
|
|
}
|
|
|
|
void
|
|
pe_free_ordering(GListPtr constraints)
|
|
{
|
|
GListPtr iterator = constraints;
|
|
while(iterator != NULL) {
|
|
order_constraint_t *order = iterator->data;
|
|
iterator = iterator->next;
|
|
|
|
crm_free(order->lh_action_task);
|
|
crm_free(order->rh_action_task);
|
|
crm_free(order);
|
|
}
|
|
if(constraints != NULL) {
|
|
g_list_free(constraints);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
pe_free_rsc_to_node(GListPtr constraints)
|
|
{
|
|
GListPtr iterator = constraints;
|
|
while(iterator != NULL) {
|
|
rsc_to_node_t *cons = iterator->data;
|
|
iterator = iterator->next;
|
|
|
|
pe_free_shallow(cons->node_list_rh);
|
|
crm_free(cons);
|
|
}
|
|
if(constraints != NULL) {
|
|
g_list_free(constraints);
|
|
}
|
|
}
|
|
|
|
|
|
rsc_to_node_t *
|
|
rsc2node_new(const char *id, resource_t *rsc,
|
|
int node_weight, node_t *foo_node, pe_working_set_t *data_set)
|
|
{
|
|
rsc_to_node_t *new_con = NULL;
|
|
|
|
if(rsc == NULL || id == NULL) {
|
|
pe_err("Invalid constraint %s for rsc=%p", crm_str(id), rsc);
|
|
return NULL;
|
|
|
|
} else if(foo_node == NULL) {
|
|
CRM_CHECK(node_weight == 0, return NULL);
|
|
}
|
|
|
|
crm_malloc0(new_con, sizeof(rsc_to_node_t));
|
|
if(new_con != NULL) {
|
|
new_con->id = id;
|
|
new_con->rsc_lh = rsc;
|
|
new_con->node_list_rh = NULL;
|
|
new_con->role_filter = RSC_ROLE_UNKNOWN;
|
|
|
|
if(foo_node != NULL) {
|
|
node_t *copy = node_copy(foo_node);
|
|
copy->weight = node_weight;
|
|
new_con->node_list_rh = g_list_append(NULL, copy);
|
|
}
|
|
|
|
data_set->placement_constraints = g_list_append(
|
|
data_set->placement_constraints, new_con);
|
|
rsc->rsc_location = g_list_append(rsc->rsc_location, new_con);
|
|
}
|
|
|
|
return new_con;
|
|
}
|
|
|
|
|
|
const char *
|
|
ordering_type2text(enum pe_ordering type)
|
|
{
|
|
const char *result = "<unknown>";
|
|
if(type & pe_order_implies_left) {
|
|
/* was: mandatory */
|
|
result = "right_implies_left";
|
|
|
|
} else if(type & pe_order_implies_right) {
|
|
/* was: recover */
|
|
result = "left_implies_right";
|
|
|
|
} else if(type & pe_order_optional) {
|
|
/* pure ordering, nothing implied */
|
|
result = "optional";
|
|
|
|
} else if(type & pe_order_runnable_left) {
|
|
result = "runnable";
|
|
|
|
} else {
|
|
crm_err("Unknown ordering type: %.3x", type);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
gboolean
|
|
can_run_resources(const node_t *node)
|
|
{
|
|
if(node == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if(node->details->online == FALSE
|
|
|| node->details->shutdown
|
|
|| node->details->unclean
|
|
|| node->details->standby) {
|
|
crm_debug_2("%s: online=%d, unclean=%d, standby=%d",
|
|
node->details->uname, node->details->online,
|
|
node->details->unclean, node->details->standby);
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/* return -1 if 'a' is more preferred
|
|
* return 1 if 'b' is more preferred
|
|
*/
|
|
gint sort_node_weight(gconstpointer a, gconstpointer b)
|
|
{
|
|
int level = LOG_DEBUG_3;
|
|
const node_t *node1 = (const node_t*)a;
|
|
const node_t *node2 = (const node_t*)b;
|
|
|
|
int node1_weight = 0;
|
|
int node2_weight = 0;
|
|
|
|
if(a == NULL) { return 1; }
|
|
if(b == NULL) { return -1; }
|
|
|
|
node1_weight = node1->weight;
|
|
node2_weight = node2->weight;
|
|
|
|
if(can_run_resources(node1) == FALSE) {
|
|
node1_weight = -INFINITY;
|
|
}
|
|
if(can_run_resources(node2) == FALSE) {
|
|
node2_weight = -INFINITY;
|
|
}
|
|
|
|
if(node1_weight > node2_weight) {
|
|
do_crm_log(level, "%s (%d) > %s (%d) : weight",
|
|
node1->details->uname, node1_weight,
|
|
node2->details->uname, node2_weight);
|
|
return -1;
|
|
}
|
|
|
|
if(node1_weight < node2_weight) {
|
|
do_crm_log(level, "%s (%d) < %s (%d) : weight",
|
|
node1->details->uname, node1_weight,
|
|
node2->details->uname, node2_weight);
|
|
return 1;
|
|
}
|
|
|
|
do_crm_log(level, "%s (%d) == %s (%d) : weight",
|
|
node1->details->uname, node1_weight,
|
|
node2->details->uname, node2_weight);
|
|
|
|
/* now try to balance resources across the cluster */
|
|
if(node1->details->num_resources
|
|
< node2->details->num_resources) {
|
|
do_crm_log(level, "%s (%d) < %s (%d) : resources",
|
|
node1->details->uname, node1->details->num_resources,
|
|
node2->details->uname, node2->details->num_resources);
|
|
return -1;
|
|
|
|
} else if(node1->details->num_resources
|
|
> node2->details->num_resources) {
|
|
do_crm_log(level, "%s (%d) > %s (%d) : resources",
|
|
node1->details->uname, node1->details->num_resources,
|
|
node2->details->uname, node2->details->num_resources);
|
|
return 1;
|
|
}
|
|
|
|
do_crm_log(level, "%s = %s", node1->details->uname, node2->details->uname);
|
|
return 0;
|
|
}
|
|
|
|
|
|
gboolean
|
|
native_assign_node(resource_t *rsc, GListPtr nodes, node_t *chosen)
|
|
{
|
|
int multiple = 0;
|
|
CRM_ASSERT(rsc->variant == pe_native);
|
|
|
|
clear_bit(rsc->flags, pe_rsc_provisional);
|
|
|
|
slist_iter(candidate, node_t, nodes, lpc,
|
|
crm_debug("Color %s, Node[%d] %s: %d", rsc->id, lpc,
|
|
candidate->details->uname, candidate->weight);
|
|
if(chosen->weight > 0
|
|
&& candidate->details->unclean == FALSE
|
|
&& candidate->weight == chosen->weight) {
|
|
multiple++;
|
|
}
|
|
);
|
|
|
|
if(chosen == NULL) {
|
|
crm_debug("Could not allocate a node for %s", rsc->id);
|
|
rsc->next_role = RSC_ROLE_STOPPED;
|
|
return FALSE;
|
|
|
|
} else if(can_run_resources(chosen) == FALSE || chosen->weight < 0) {
|
|
crm_debug("All nodes for resource %s are unavailable"
|
|
", unclean or shutting down", rsc->id);
|
|
rsc->next_role = RSC_ROLE_STOPPED;
|
|
return FALSE;
|
|
}
|
|
|
|
if(rsc->next_role == RSC_ROLE_UNKNOWN) {
|
|
rsc->next_role = RSC_ROLE_STARTED;
|
|
}
|
|
|
|
if(multiple > 1) {
|
|
int log_level = LOG_INFO;
|
|
char *score = score2char(chosen->weight);
|
|
if(chosen->weight >= INFINITY) {
|
|
log_level = LOG_WARNING;
|
|
}
|
|
|
|
do_crm_log(log_level, "%d nodes with equal score (%s) for"
|
|
" running the listed resources (chose %s):",
|
|
multiple, score, chosen->details->uname);
|
|
crm_free(score);
|
|
}
|
|
|
|
/* todo: update the old node for each resource to reflect its
|
|
* new resource count
|
|
*/
|
|
|
|
if(rsc->allocated_to) {
|
|
node_t *old = rsc->allocated_to;
|
|
old->details->allocated_rsc = g_list_remove(
|
|
old->details->allocated_rsc, rsc);
|
|
old->details->num_resources--;
|
|
old->count--;
|
|
}
|
|
|
|
crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id);
|
|
crm_free(rsc->allocated_to);
|
|
rsc->allocated_to = node_copy(chosen);
|
|
|
|
chosen->details->allocated_rsc = g_list_append(chosen->details->allocated_rsc, rsc);
|
|
chosen->details->num_resources++;
|
|
chosen->count++;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void
|
|
convert_non_atomic_task(resource_t *rsc, order_constraint_t *order)
|
|
{
|
|
int interval = 0;
|
|
char *rid = NULL;
|
|
char *raw_task = NULL;
|
|
int task = no_action;
|
|
char *old_uuid = order->lh_action_task;
|
|
|
|
crm_debug("Processing %s", old_uuid);
|
|
|
|
if(order->lh_action_task == NULL
|
|
|| strstr(order->lh_action_task, "notify") != NULL) {
|
|
/* no conversion */
|
|
return;
|
|
}
|
|
|
|
CRM_ASSERT(parse_op_key(old_uuid, &rid, &raw_task, &interval));
|
|
|
|
task = text2task(raw_task);
|
|
switch(task) {
|
|
case stop_rsc:
|
|
case start_rsc:
|
|
case action_notify:
|
|
case action_promote:
|
|
case action_demote:
|
|
break;
|
|
case stopped_rsc:
|
|
case started_rsc:
|
|
case action_notified:
|
|
case action_promoted:
|
|
case action_demoted:
|
|
task--;
|
|
break;
|
|
case monitor_rsc:
|
|
case shutdown_crm:
|
|
case stonith_node:
|
|
task = no_action;
|
|
break;
|
|
default:
|
|
crm_err("Unknown action: %s", raw_task);
|
|
task = no_action;
|
|
break;
|
|
}
|
|
|
|
if(task != no_action) {
|
|
if(is_set(rsc->flags, pe_rsc_notify)) {
|
|
order->lh_action_task = generate_notify_key(
|
|
rsc->id, "confirmed-post",
|
|
task2text(task));
|
|
} else {
|
|
order->lh_action_task = generate_op_key(
|
|
rsc->id, task2text(task+1), 0);
|
|
}
|
|
crm_debug("Converted %s -> %s",
|
|
old_uuid, order->lh_action_task);
|
|
crm_free(old_uuid);
|
|
}
|
|
|
|
crm_free(raw_task);
|
|
crm_free(rid);
|
|
}
|
|
|
|
|
|
void
|
|
order_actions(
|
|
action_t *lh_action, action_t *rh_action, enum pe_ordering order)
|
|
{
|
|
action_wrapper_t *wrapper = NULL;
|
|
GListPtr list = NULL;
|
|
|
|
crm_debug_2("Ordering Action %s before %s",
|
|
lh_action->uuid, rh_action->uuid);
|
|
|
|
log_action(LOG_DEBUG_4, "LH (order_actions)", lh_action, FALSE);
|
|
log_action(LOG_DEBUG_4, "RH (order_actions)", rh_action, FALSE);
|
|
|
|
|
|
crm_malloc0(wrapper, sizeof(action_wrapper_t));
|
|
wrapper->action = rh_action;
|
|
wrapper->type = order;
|
|
|
|
list = lh_action->actions_after;
|
|
list = g_list_append(list, wrapper);
|
|
lh_action->actions_after = list;
|
|
|
|
wrapper = NULL;
|
|
|
|
/* order |= pe_order_implies_right; */
|
|
/* order ^= pe_order_implies_right; */
|
|
|
|
crm_malloc0(wrapper, sizeof(action_wrapper_t));
|
|
wrapper->action = lh_action;
|
|
wrapper->type = order;
|
|
list = rh_action->actions_before;
|
|
list = g_list_append(list, wrapper);
|
|
rh_action->actions_before = list;
|
|
}
|
|
|
|
|
|
void
|
|
log_action(unsigned int log_level, const char *pre_text, action_t *action, gboolean details)
|
|
{
|
|
const char *node_uname = NULL;
|
|
const char *node_uuid = NULL;
|
|
|
|
if(action == NULL) {
|
|
|
|
do_crm_log(log_level, "%s%s: <NULL>",
|
|
pre_text==NULL?"":pre_text,
|
|
pre_text==NULL?"":": ");
|
|
return;
|
|
}
|
|
|
|
|
|
if(action->pseudo) {
|
|
node_uname = NULL;
|
|
node_uuid = NULL;
|
|
|
|
} else if(action->node != NULL) {
|
|
node_uname = action->node->details->uname;
|
|
node_uuid = action->node->details->id;
|
|
} else {
|
|
node_uname = "<none>";
|
|
node_uuid = NULL;
|
|
}
|
|
|
|
switch(text2task(action->task)) {
|
|
case stonith_node:
|
|
case shutdown_crm:
|
|
do_crm_log(log_level,
|
|
"%s%s%sAction %d: %s%s%s%s%s%s",
|
|
pre_text==NULL?"":pre_text,
|
|
pre_text==NULL?"":": ",
|
|
action->pseudo?"Pseduo ":action->optional?"Optional ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ",
|
|
action->id, action->uuid,
|
|
node_uname?"\ton ":"",
|
|
node_uname?node_uname:"",
|
|
node_uuid?"\t\t(":"",
|
|
node_uuid?node_uuid:"",
|
|
node_uuid?")":"");
|
|
break;
|
|
default:
|
|
do_crm_log(log_level,
|
|
"%s%s%sAction %d: %s %s%s%s%s%s%s",
|
|
pre_text==NULL?"":pre_text,
|
|
pre_text==NULL?"":": ",
|
|
action->optional?"Optional ":action->pseudo?"Pseduo ":action->runnable?action->processed?"":"(Provisional) ":"!!Non-Startable!! ",
|
|
action->id, action->uuid,
|
|
safe_val3("<none>", action, rsc, id),
|
|
node_uname?"\ton ":"",
|
|
node_uname?node_uname:"",
|
|
node_uuid?"\t\t(":"",
|
|
node_uuid?node_uuid:"",
|
|
node_uuid?")":"");
|
|
|
|
break;
|
|
}
|
|
|
|
if(details) {
|
|
do_crm_log(log_level+1, "\t\t====== Preceeding Actions");
|
|
slist_iter(
|
|
other, action_wrapper_t, action->actions_before, lpc,
|
|
log_action(log_level+1, "\t\t", other->action, FALSE);
|
|
);
|
|
do_crm_log(log_level+1, "\t\t====== Subsequent Actions");
|
|
slist_iter(
|
|
other, action_wrapper_t, action->actions_after, lpc,
|
|
log_action(log_level+1, "\t\t", other->action, FALSE);
|
|
);
|
|
do_crm_log(log_level+1, "\t\t====== End");
|
|
|
|
} else {
|
|
do_crm_log(log_level, "\t\t(seen=%d, before=%d, after=%d)",
|
|
action->seen_count,
|
|
g_list_length(action->actions_before),
|
|
g_list_length(action->actions_after));
|
|
}
|
|
}
|
|
|
|
resource_t *uber_parent(resource_t *rsc)
|
|
{
|
|
resource_t *parent = rsc;
|
|
while(parent != NULL && parent->parent != NULL) {
|
|
parent = parent->parent;
|
|
}
|
|
return parent;
|
|
}
|
|
|
|
|
|
action_t *get_pseudo_op(const char *name, pe_working_set_t *data_set)
|
|
{
|
|
action_t *op = NULL;
|
|
const char *op_s = name;
|
|
GListPtr possible_matches = NULL;
|
|
|
|
possible_matches = find_actions(data_set->actions, name, NULL);
|
|
if(possible_matches != NULL) {
|
|
if(g_list_length(possible_matches) > 1) {
|
|
pe_warn("Action %s exists %d times",
|
|
name, g_list_length(possible_matches));
|
|
}
|
|
|
|
op = g_list_nth_data(possible_matches, 0);
|
|
g_list_free(possible_matches);
|
|
|
|
} else {
|
|
op = custom_action(NULL, crm_strdup(op_s), op_s,
|
|
NULL, TRUE, TRUE, data_set);
|
|
op->pseudo = TRUE;
|
|
op->runnable = TRUE;
|
|
}
|
|
|
|
return op;
|
|
}
|
|
|
|
gboolean can_run_any(GListPtr nodes)
|
|
{
|
|
if(nodes == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
slist_iter(
|
|
node, node_t, nodes, lpc,
|
|
if(can_run_resources(node) && node->weight >= 0) {
|
|
return TRUE;
|
|
}
|
|
);
|
|
|
|
return FALSE;
|
|
}
|
|
|