blob: 3611ed134200dbf06dd9364376089593079a2e25 [file] [log] [blame]
/*
* Copyright 2004-2019 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>
pe__location_t *
rsc2node_new(const char *id, pe_resource_t *rsc,
int node_weight, const char *discover_mode,
pe_node_t *foo_node, pe_working_set_t *data_set)
{
pe__location_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);
}
new_con = calloc(1, sizeof(pe__location_t));
if (new_con != NULL) {
new_con->id = strdup(id);
new_con->rsc_lh = rsc;
new_con->node_list_rh = NULL;
new_con->role_filter = RSC_ROLE_UNKNOWN;
if (discover_mode == NULL || safe_str_eq(discover_mode, "always")) {
new_con->discover_mode = pe_discover_always;
} else if (safe_str_eq(discover_mode, "never")) {
new_con->discover_mode = pe_discover_never;
} else if (safe_str_eq(discover_mode, "exclusive")) {
new_con->discover_mode = pe_discover_exclusive;
rsc->exclusive_discover = TRUE;
} else {
pe_err("Invalid %s value %s in location constraint", XML_LOCATION_ATTR_DISCOVERY, discover_mode);
}
if (foo_node != NULL) {
node_t *copy = node_copy(foo_node);
copy->weight = node_weight;
new_con->node_list_rh = g_list_prepend(NULL, copy);
}
data_set->placement_constraints = g_list_prepend(data_set->placement_constraints, new_con);
rsc->rsc_location = g_list_prepend(rsc->rsc_location, new_con);
}
return new_con;
}
gboolean
can_run_resources(const node_t * node)
{
if (node == NULL) {
return FALSE;
}
#if 0
if (node->weight < 0) {
return FALSE;
}
#endif
if (node->details->online == FALSE
|| node->details->shutdown || node->details->unclean
|| node->details->standby || node->details->maintenance) {
crm_trace("%s: online=%d, unclean=%d, standby=%d, maintenance=%d",
node->details->uname, node->details->online,
node->details->unclean, node->details->standby, node->details->maintenance);
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, gpointer data)
{
const node_t *node1 = (const node_t *)a;
const node_t *node2 = (const node_t *)b;
const node_t *active = (node_t *) data;
int node1_weight = 0;
int node2_weight = 0;
int result = 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) {
crm_trace("%s (%d) > %s (%d) : weight",
node1->details->uname, node1_weight, node2->details->uname, node2_weight);
return -1;
}
if (node1_weight < node2_weight) {
crm_trace("%s (%d) < %s (%d) : weight",
node1->details->uname, node1_weight, node2->details->uname, node2_weight);
return 1;
}
crm_trace("%s (%d) == %s (%d) : weight",
node1->details->uname, node1_weight, node2->details->uname, node2_weight);
if (safe_str_eq(pe_dataset->placement_strategy, "minimal")) {
goto equal;
}
if (safe_str_eq(pe_dataset->placement_strategy, "balanced")) {
result = compare_capacity(node1, node2);
if (result < 0) {
crm_trace("%s > %s : capacity (%d)",
node1->details->uname, node2->details->uname, result);
return -1;
} else if (result > 0) {
crm_trace("%s < %s : capacity (%d)",
node1->details->uname, node2->details->uname, result);
return 1;
}
}
/* now try to balance resources across the cluster */
if (node1->details->num_resources < node2->details->num_resources) {
crm_trace("%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) {
crm_trace("%s (%d) < %s (%d) : resources",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return 1;
}
if (active && active->details == node1->details) {
crm_trace("%s (%d) > %s (%d) : active",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return -1;
} else if (active && active->details == node2->details) {
crm_trace("%s (%d) < %s (%d) : active",
node1->details->uname, node1->details->num_resources,
node2->details->uname, node2->details->num_resources);
return 1;
}
equal:
crm_trace("%s = %s", node1->details->uname, node2->details->uname);
return strcmp(node1->details->uname, node2->details->uname);
}
void
native_deallocate(resource_t * rsc)
{
if (rsc->allocated_to) {
node_t *old = rsc->allocated_to;
crm_info("Deallocating %s from %s", rsc->id, old->details->uname);
set_bit(rsc->flags, pe_rsc_provisional);
rsc->allocated_to = NULL;
old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc, rsc);
old->details->num_resources--;
/* old->count--; */
calculate_utilization(old->details->utilization, rsc->utilization, TRUE);
free(old);
}
}
gboolean
native_assign_node(resource_t * rsc, GListPtr nodes, node_t * chosen, gboolean force)
{
CRM_ASSERT(rsc->variant == pe_native);
if (force == FALSE && chosen != NULL) {
bool unset = FALSE;
if(chosen->weight < 0) {
unset = TRUE;
// Allow the graph to assume that the remote resource will come up
} else if(can_run_resources(chosen) == FALSE && !is_container_remote_node(chosen)) {
unset = TRUE;
}
if(unset) {
crm_debug("All nodes for resource %s are unavailable"
", unclean or shutting down (%s: %d, %d)",
rsc->id, chosen->details->uname, can_run_resources(chosen), chosen->weight);
rsc->next_role = RSC_ROLE_STOPPED;
chosen = NULL;
}
}
/* todo: update the old node for each resource to reflect its
* new resource count
*/
native_deallocate(rsc);
clear_bit(rsc->flags, pe_rsc_provisional);
if (chosen == NULL) {
GListPtr gIter = NULL;
char *rc_inactive = crm_itoa(PCMK_OCF_NOT_RUNNING);
crm_debug("Could not allocate a node for %s", rsc->id);
rsc->next_role = RSC_ROLE_STOPPED;
for (gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
action_t *op = (action_t *) gIter->data;
const char *interval = g_hash_table_lookup(op->meta, XML_LRM_ATTR_INTERVAL);
crm_debug("Processing %s", op->uuid);
if(safe_str_eq(RSC_STOP, op->task)) {
update_action_flags(op, pe_action_optional | pe_action_clear, __FUNCTION__, __LINE__);
} else if(safe_str_eq(RSC_START, op->task)) {
update_action_flags(op, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__);
/* set_bit(rsc->flags, pe_rsc_block); */
} else if(interval && safe_str_neq(interval, "0")) {
if(safe_str_eq(rc_inactive, g_hash_table_lookup(op->meta, XML_ATTR_TE_TARGET_RC))) {
/* This is a recurring monitor for the stopped state, leave it alone */
} else {
/* Normal monitor operation, cancel it */
update_action_flags(op, pe_action_runnable | pe_action_clear, __FUNCTION__, __LINE__);
}
}
}
free(rc_inactive);
return FALSE;
}
crm_debug("Assigning %s to %s", chosen->details->uname, rsc->id);
rsc->allocated_to = node_copy(chosen);
chosen->details->allocated_rsc = g_list_prepend(chosen->details->allocated_rsc, rsc);
chosen->details->num_resources++;
chosen->count++;
calculate_utilization(chosen->details->utilization, rsc->utilization, FALSE);
dump_rsc_utilization(show_utilization ? 0 : utilization_log_level, __FUNCTION__, rsc, chosen);
return TRUE;
}
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) {
crm_trace("%s%s: <NULL>", pre_text == NULL ? "" : pre_text, pre_text == NULL ? "" : ": ");
return;
}
if (is_set(action->flags, pe_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:
crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
pre_text == NULL ? "" : pre_text,
pre_text == NULL ? "" : ": ",
is_set(action->flags,
pe_action_pseudo) ? "Pseudo " : is_set(action->flags,
pe_action_optional) ?
"Optional " : is_set(action->flags,
pe_action_runnable) ? is_set(action->flags,
pe_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:
crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
pre_text == NULL ? "" : pre_text,
pre_text == NULL ? "" : ": ",
is_set(action->flags,
pe_action_optional) ? "Optional " : is_set(action->flags,
pe_action_pseudo)
? "Pseudo " : is_set(action->flags,
pe_action_runnable) ? is_set(action->flags,
pe_action_processed)
? "" : "(Provisional) " : "!!Non-Startable!! ", action->id,
action->uuid, action->rsc ? action->rsc->id : "<none>",
node_uname ? "\ton " : "", node_uname ? node_uname : "",
node_uuid ? "\t\t(" : "", node_uuid ? node_uuid : "", node_uuid ? ")" : "");
break;
}
if (details) {
GListPtr gIter = NULL;
crm_trace("\t\t====== Preceding Actions");
gIter = action->actions_before;
for (; gIter != NULL; gIter = gIter->next) {
action_wrapper_t *other = (action_wrapper_t *) gIter->data;
log_action(log_level + 1, "\t\t", other->action, FALSE);
}
crm_trace("\t\t====== Subsequent Actions");
gIter = action->actions_after;
for (; gIter != NULL; gIter = gIter->next) {
action_wrapper_t *other = (action_wrapper_t *) gIter->data;
log_action(log_level + 1, "\t\t", other->action, FALSE);
}
crm_trace("\t\t====== End");
} else {
crm_trace("\t\t(seen=%d, before=%d, after=%d)",
action->seen_count,
g_list_length(action->actions_before), g_list_length(action->actions_after));
}
}
gboolean
can_run_any(GHashTable * nodes)
{
GHashTableIter iter;
node_t *node = NULL;
if (nodes == NULL) {
return FALSE;
}
g_hash_table_iter_init(&iter, nodes);
while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
if (can_run_resources(node) && node->weight >= 0) {
return TRUE;
}
}
return FALSE;
}
pe_action_t *
create_pseudo_resource_op(resource_t * rsc, const char *task, bool optional, bool runnable, pe_working_set_t *data_set)
{
pe_action_t *action = custom_action(rsc, generate_op_key(rsc->id, task, 0), task, NULL, optional, TRUE, data_set);
update_action_flags(action, pe_action_pseudo, __FUNCTION__, __LINE__);
update_action_flags(action, pe_action_runnable, __FUNCTION__, __LINE__);
if(runnable) {
update_action_flags(action, pe_action_runnable, __FUNCTION__, __LINE__);
}
return action;
}
/*!
* \internal
* \brief Create a shutdown op for a scheduler transition
*
* \param[in] rsc Resource of action to cancel
* \param[in] task Name of action to cancel
* \param[in] interval_ms Interval of action to cancel
* \param[in] node Node of action to cancel
* \param[in] data_set Working set of cluster
*
* \return Created op
*/
pe_action_t *
sched_shutdown_op(pe_node_t *node, pe_working_set_t *data_set)
{
char *shutdown_id = crm_strdup_printf("%s-%s", CRM_OP_SHUTDOWN,
node->details->uname);
pe_action_t *shutdown_op = custom_action(NULL, shutdown_id, CRM_OP_SHUTDOWN,
node, FALSE, TRUE, data_set);
crm_notice("Scheduling shutdown of node %s", node->details->uname);
shutdown_constraints(node, shutdown_op, data_set);
add_hash_param(shutdown_op->meta, XML_ATTR_TE_NOWAIT, XML_BOOLEAN_TRUE);
return shutdown_op;
}