blob: e3ec5a00d0d83d2eb22fdd0d5d23fff122a0b21f [file] [log] [blame]
/*
* Copyright 2004-2018 Andrew Beekhof <andrew@beekhof.net>
*
* This source code is licensed under the GNU General Public License version 2
* or later (GPLv2+) WITHOUT ANY WARRANTY.
*/
#include <crm_internal.h>
#include <pengine.h>
#include <crm/msg_xml.h>
#include <allocate.h>
#include <utils.h>
#define VARIANT_GROUP 1
#include <lib/pengine/variant.h>
pe_node_t *
pcmk__group_allocate(pe_resource_t *rsc, pe_node_t *prefer,
pe_working_set_t *data_set)
{
node_t *node = NULL;
node_t *group_node = NULL;
GListPtr gIter = NULL;
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;
}
if (is_set(rsc->flags, pe_rsc_allocating)) {
pe_rsc_debug(rsc, "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->last_child->rsc_cons_lhs =
g_list_concat(group_data->last_child->rsc_cons_lhs, rsc->rsc_cons_lhs);
rsc->rsc_cons_lhs = NULL;
pe__show_node_weights(!show_scores, rsc, __FUNCTION__, rsc->allowed_nodes);
gIter = rsc->children;
for (; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
pe_rsc_trace(rsc, "Allocating group %s member %s",
rsc->id, child_rsc->id);
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;
pe_rsc_trace(rsc, "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(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(op->flags, pe_action_pseudo | pe_action_runnable);
op = stop_action(rsc, NULL, TRUE /* !group_data->child_stopping */ );
set_bit(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(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(op->flags, pe_action_pseudo);
set_bit(op->flags, pe_action_runnable);
op = custom_action(rsc, demoted_key(rsc), RSC_DEMOTED, NULL, TRUE, TRUE, data_set);
set_bit(op->flags, pe_action_pseudo);
set_bit(op->flags, pe_action_runnable);
op = custom_action(rsc, promote_key(rsc), RSC_PROMOTE, NULL, TRUE, TRUE, data_set);
set_bit(op->flags, pe_action_pseudo);
set_bit(op->flags, pe_action_runnable);
op = custom_action(rsc, promoted_key(rsc), RSC_PROMOTED, NULL, TRUE, TRUE, data_set);
set_bit(op->flags, pe_action_pseudo);
set_bit(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;
pe_rsc_trace(action->rsc, "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;
pe_rsc_trace(action->rsc, "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 *last_active = 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 | pe_order_restart);
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);
}
}
/* Look for partially active groups
* Make sure they still shut down in sequence
*/
if (child_rsc->running_on) {
if (group_data->ordered
&& last_rsc
&& last_rsc->running_on == NULL && last_active && last_active->running_on) {
order_stop_stop(child_rsc, last_active, pe_order_optional);
}
last_active = child_rsc;
}
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;
}
if (constraint->score == 0) {
return;
}
gIter = rsc_lh->children;
pe_rsc_trace(rsc_lh, "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 mandatory 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);
if (constraint->score == 0) {
return;
}
pe_rsc_trace(rsc_rh, "Processing RH %s of constraint %s (LH is %s)",
rsc_rh->id, constraint->id, rsc_lh->id);
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 mandatory 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)
{
GListPtr gIter = NULL;
enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
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) {
pe_rsc_trace(action->rsc, "%s is mandatory because of %s", action->uuid,
child_action->uuid);
clear_bit(flags, pe_action_optional);
pe_clear_action_bit(action, pe_action_optional);
}
if (safe_str_neq(task_s, action->task)
&& is_set(flags, pe_action_runnable)
&& is_set(child_flags, pe_action_runnable) == FALSE) {
pe_rsc_trace(action->rsc, "%s is not runnable because of %s", action->uuid,
child_action->uuid);
clear_bit(flags, pe_action_runnable);
pe_clear_action_bit(action, pe_action_runnable);
}
} else if (task != stop_rsc && task != action_demote) {
pe_rsc_trace(action->rsc, "%s is not runnable because of %s (not found in %s)",
action->uuid, task_s, child->id);
clear_bit(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(pe_resource_t *rsc, pe__location_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);
pe_rsc_debug(rsc, "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;
g_list_free_full(zero, free);
}
void
group_expand(resource_t * rsc, pe_working_set_t * data_set)
{
CRM_CHECK(rsc != NULL, return);
pe_rsc_trace(rsc, "Processing actions from %s", rsc->id);
native_expand(rsc, data_set);
for (GListPtr gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
resource_t *child_rsc = (resource_t *) gIter->data;
child_rsc->cmds->expand(child_rsc, data_set);
}
}
GHashTable *
pcmk__group_merge_weights(pe_resource_t *rsc, const char *rhs,
GHashTable *nodes, const char *attr, float factor,
uint32_t flags)
{
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)) {
pe_rsc_info(rsc, "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, flags);
for (; gIter != NULL; gIter = gIter->next) {
rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
if (constraint->score == 0) {
continue;
}
nodes = pcmk__native_merge_weights(constraint->rsc_lh, rsc->id, nodes,
constraint->node_attribute,
constraint->score / (float) INFINITY,
flags);
}
clear_bit(rsc->flags, pe_rsc_merging);
return nodes;
}
void
group_append_meta(resource_t * rsc, xmlNode * xml)
{
}