diff --git a/lib/pacemaker/pcmk_sched_nodes.c b/lib/pacemaker/pcmk_sched_nodes.c index ca0196212b..f1a91664b2 100644 --- a/lib/pacemaker/pcmk_sched_nodes.c +++ b/lib/pacemaker/pcmk_sched_nodes.c @@ -1,219 +1,238 @@ /* * Copyright 2004-2021 the Pacemaker project contributors * * The version control history for this file may have further details. * * This source code is licensed under the GNU General Public License version 2 * or later (GPLv2+) WITHOUT ANY WARRANTY. */ #include #include #include // lrmd_event_data_t #include #include #include #include "libpacemaker_private.h" /*! * \internal * \brief Check whether a node is available to run resources * * \param[in] node Node to check * * \return true if node is online and not shutting down, unclean, or in standby * or maintenance mode, otherwise false */ bool pcmk__node_available(const pe_node_t *node) { // @TODO Should we add (node->weight >= 0)? return (node != NULL) && (node->details != NULL) && node->details->online && !node->details->shutdown && !node->details->unclean && !node->details->standby && !node->details->maintenance; } /*! * \internal * \brief Copy a hash table of node objects * * \param[in] nodes Hash table to copy * * \return New copy of nodes (or NULL if nodes is NULL) */ GHashTable * pcmk__copy_node_table(GHashTable *nodes) { GHashTable *new_table = NULL; GHashTableIter iter; pe_node_t *node = NULL; if (nodes == NULL) { return NULL; } new_table = pcmk__strkey_table(NULL, free); g_hash_table_iter_init(&iter, nodes); while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) { pe_node_t *new_node = pe__copy_node(node); g_hash_table_insert(new_table, (gpointer) new_node->details->id, new_node); } return new_table; } /*! * \internal * \brief Copy a list of node objects * * \param[in] list List to copy * \param[in] reset Set copies' scores to 0 * * \return New list of shallow copies of nodes in original list */ GList * pcmk__copy_node_list(const GList *list, bool reset) { GList *result = NULL; for (const GList *gIter = list; gIter != NULL; gIter = gIter->next) { pe_node_t *new_node = NULL; pe_node_t *this_node = (pe_node_t *) gIter->data; new_node = pe__copy_node(this_node); if (reset) { new_node->weight = 0; } result = g_list_prepend(result, new_node); } return result; } struct node_weight_s { pe_node_t *active; pe_working_set_t *data_set; }; -/* return -1 if 'a' is more preferred - * return 1 if 'b' is more preferred +/*! + * \internal + * \brief Compare two nodes for allocation desirability + * + * Given two nodes, check which one is more preferred by allocation criteria + * such as node weight and utilization. + * + * \param[in] a First node to compare + * \param[in] b Second node to compare + * \param[in] data Sort data (as struct node_weight_s *) + * + * \return -1 if \p a is preferred, +1 if \p b is preferred, or 0 if they are + * equally preferred */ - static gint -sort_node_weight(gconstpointer a, gconstpointer b, gpointer data) +compare_nodes(gconstpointer a, gconstpointer b, gpointer data) { - const pe_node_t *node1 = (const pe_node_t *)a; - const pe_node_t *node2 = (const pe_node_t *)b; + const pe_node_t *node1 = (const pe_node_t *) a; + const pe_node_t *node2 = (const pe_node_t *) b; struct node_weight_s *nw = 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; + // Compare node weights - if (!pcmk__node_available(node1)) { - node1_weight = -INFINITY; - } - if (!pcmk__node_available(node2)) { - node2_weight = -INFINITY; - } + node1_weight = pcmk__node_available(node1)? node1->weight : -INFINITY; + node2_weight = pcmk__node_available(node2)? 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); + 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); + 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); + node1->details->uname, node1_weight, node2->details->uname, + node2_weight); + + // If appropriate, compare node utilization - if (pcmk__str_eq(nw->data_set->placement_strategy, "minimal", pcmk__str_casei)) { + if (pcmk__str_eq(nw->data_set->placement_strategy, "minimal", + pcmk__str_casei)) { goto equal; } - if (pcmk__str_eq(nw->data_set->placement_strategy, "balanced", pcmk__str_casei)) { + if (pcmk__str_eq(nw->data_set->placement_strategy, "balanced", + pcmk__str_casei)) { 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 */ + // Compare number of allocated resources + 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 (nw->active && nw->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 (nw->active && nw->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; + // Check whether one node is already running desired resource + + if (nw->active != NULL) { + if (nw->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 (nw->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: + + // If all else is equal, prefer node with lowest-sorting name +equal: crm_trace("%s = %s", node1->details->uname, node2->details->uname); return strcmp(node1->details->uname, node2->details->uname); } GList * sort_nodes_by_weight(GList *nodes, pe_node_t *active_node, pe_working_set_t *data_set) { struct node_weight_s nw = { active_node, data_set }; - return g_list_sort_with_data(nodes, sort_node_weight, &nw); + return g_list_sort_with_data(nodes, compare_nodes, &nw); } gboolean can_run_any(GHashTable * nodes) { GHashTableIter iter; pe_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 (pcmk__node_available(node) && (node->weight >= 0)) { return TRUE; } } return FALSE; }