pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pcmk_sched_nodes.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 the Pacemaker project contributors
3  *
4  * The version control history for this file may have further details.
5  *
6  * This source code is licensed under the GNU General Public License version 2
7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
8  */
9 
10 #include <crm_internal.h>
11 #include <crm/common/xml.h>
13 #include <pacemaker-internal.h>
14 #include <pacemaker.h>
15 #include "libpacemaker_private.h"
16 
29 bool
30 pcmk__node_available(const pcmk_node_t *node, bool consider_score,
31  bool consider_guest)
32 {
33  if ((node == NULL) || (node->details == NULL) || !node->details->online
34  || node->details->shutdown || node->details->unclean
36  || node->details->maintenance) {
37  return false;
38  }
39 
40  if (consider_score && (node->assign->score < 0)) {
41  return false;
42  }
43 
44  // @TODO Go through all callers to see which should set consider_guest
45  if (consider_guest && pcmk__is_guest_or_bundle_node(node)) {
46  pcmk_resource_t *guest = node->priv->remote->priv->launcher;
47 
48  if (guest->priv->fns->location(guest, NULL,
49  pcmk__rsc_node_assigned) == NULL) {
50  return false;
51  }
52  }
53 
54  return true;
55 }
56 
66 GHashTable *
67 pcmk__copy_node_table(GHashTable *nodes)
68 {
69  GHashTable *new_table = NULL;
70  GHashTableIter iter;
71  pcmk_node_t *node = NULL;
72 
73  if (nodes == NULL) {
74  return NULL;
75  }
76  new_table = pcmk__strkey_table(NULL, pcmk__free_node_copy);
77  g_hash_table_iter_init(&iter, nodes);
78  while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
79  pcmk_node_t *new_node = pe__copy_node(node);
80 
81  g_hash_table_insert(new_table, (gpointer) new_node->priv->id,
82  new_node);
83  }
84  return new_table;
85 }
86 
95 static void
96 destroy_node_tables(gpointer data)
97 {
98  g_hash_table_destroy((GHashTable *) data);
99 }
100 
116 void
117 pcmk__copy_node_tables(const pcmk_resource_t *rsc, GHashTable **copy)
118 {
119  pcmk__assert((rsc != NULL) && (copy != NULL));
120 
121  if (*copy == NULL) {
122  *copy = pcmk__strkey_table(NULL, destroy_node_tables);
123  }
124 
125  g_hash_table_insert(*copy, rsc->id,
127 
128  for (const GList *iter = rsc->priv->children;
129  iter != NULL; iter = iter->next) {
130 
131  pcmk__copy_node_tables((const pcmk_resource_t *) iter->data, copy);
132  }
133 }
134 
149 void
151 {
152  pcmk__assert((rsc != NULL) && (backup != NULL));
153 
154  g_hash_table_destroy(rsc->priv->allowed_nodes);
155 
156  // Copy to avoid danger with multiple restores
157  rsc->priv->allowed_nodes =
158  pcmk__copy_node_table(g_hash_table_lookup(backup, rsc->id));
159 
160  for (GList *iter = rsc->priv->children;
161  iter != NULL; iter = iter->next) {
162 
163  pcmk__restore_node_tables((pcmk_resource_t *) iter->data, backup);
164  }
165 }
166 
176 GList *
177 pcmk__copy_node_list(const GList *list, bool reset)
178 {
179  GList *result = NULL;
180 
181  for (const GList *iter = list; iter != NULL; iter = iter->next) {
182  pcmk_node_t *new_node = NULL;
183  pcmk_node_t *this_node = iter->data;
184 
185  new_node = pe__copy_node(this_node);
186  if (reset) {
187  new_node->assign->score = 0;
188  }
189  result = g_list_prepend(result, new_node);
190  }
191  return result;
192 }
193 
208 static gint
209 compare_nodes(gconstpointer a, gconstpointer b, gpointer data)
210 {
211  const pcmk_node_t *node1 = (const pcmk_node_t *) a;
212  const pcmk_node_t *node2 = (const pcmk_node_t *) b;
213  const pcmk_node_t *preferred = (const pcmk_node_t *) data;
214 
215  int node1_score = -PCMK_SCORE_INFINITY;
216  int node2_score = -PCMK_SCORE_INFINITY;
217 
218  int result = 0;
219 
220  if (a == NULL) {
221  return 1;
222  }
223  if (b == NULL) {
224  return -1;
225  }
226 
227  // Compare node scores
228 
229  if (pcmk__node_available(node1, false, false)) {
230  node1_score = node1->assign->score;
231  }
232  if (pcmk__node_available(node2, false, false)) {
233  node2_score = node2->assign->score;
234  }
235 
236  if (node1_score > node2_score) {
237  crm_trace("%s before %s (score %d > %d)",
238  pcmk__node_name(node1), pcmk__node_name(node2),
239  node1_score, node2_score);
240  return -1;
241  }
242 
243  if (node1_score < node2_score) {
244  crm_trace("%s after %s (score %d < %d)",
245  pcmk__node_name(node1), pcmk__node_name(node2),
246  node1_score, node2_score);
247  return 1;
248  }
249 
250  // If appropriate, compare node utilization
251 
252  if (pcmk__str_eq(node1->priv->scheduler->priv->placement_strategy,
254  goto equal;
255  }
256 
257  if (pcmk__str_eq(node1->priv->scheduler->priv->placement_strategy,
259 
261  if (result < 0) {
262  crm_trace("%s before %s (greater capacity by %d attributes)",
263  pcmk__node_name(node1), pcmk__node_name(node2),
264  result * -1);
265  return -1;
266  } else if (result > 0) {
267  crm_trace("%s after %s (lower capacity by %d attributes)",
268  pcmk__node_name(node1), pcmk__node_name(node2), result);
269  return 1;
270  }
271  }
272 
273  // Compare number of resources already assigned to node
274 
276  crm_trace("%s before %s (%d resources < %d)",
277  pcmk__node_name(node1), pcmk__node_name(node2),
279  return -1;
280 
281  } else if (node1->priv->num_resources > node2->priv->num_resources) {
282  crm_trace("%s after %s (%d resources > %d)",
283  pcmk__node_name(node1), pcmk__node_name(node2),
285  return 1;
286  }
287 
288  // Check whether one node is already running desired resource
289 
290  if (preferred != NULL) {
291  if (pcmk__same_node(preferred, node1)) {
292  crm_trace("%s before %s (preferred node)",
293  pcmk__node_name(node1), pcmk__node_name(node2));
294  return -1;
295  } else if (pcmk__same_node(preferred, node2)) {
296  crm_trace("%s after %s (not preferred node)",
297  pcmk__node_name(node1), pcmk__node_name(node2));
298  return 1;
299  }
300  }
301 
302  // If all else is equal, prefer node with lowest-sorting name
303 equal:
304  result = strcmp(node1->priv->name, node2->priv->name);
305  if (result < 0) {
306  crm_trace("%s before %s (name)",
307  pcmk__node_name(node1), pcmk__node_name(node2));
308  return -1;
309  } else if (result > 0) {
310  crm_trace("%s after %s (name)",
311  pcmk__node_name(node1), pcmk__node_name(node2));
312  return 1;
313  }
314 
315  crm_trace("%s == %s", pcmk__node_name(node1), pcmk__node_name(node2));
316  return 0;
317 }
318 
328 GList *
329 pcmk__sort_nodes(GList *nodes, pcmk_node_t *active_node)
330 {
331  return g_list_sort_with_data(nodes, compare_nodes, active_node);
332 }
333 
343 bool
344 pcmk__any_node_available(GHashTable *nodes)
345 {
346  GHashTableIter iter;
347  const pcmk_node_t *node = NULL;
348 
349  if (nodes == NULL) {
350  return false;
351  }
352  g_hash_table_iter_init(&iter, nodes);
353  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
354  if (pcmk__node_available(node, true, false)) {
355  return true;
356  }
357  }
358  return false;
359 }
360 
367 void
369 {
370  int base_health = 0;
371  enum pcmk__health_strategy strategy;
372  const char *strategy_str =
375 
376  strategy = pcmk__parse_health_strategy(strategy_str);
377  if (strategy == pcmk__health_strategy_none) {
378  return;
379  }
380  crm_info("Applying node health strategy '%s'", strategy_str);
381 
382  // The progressive strategy can use a base health score
383  if (strategy == pcmk__health_strategy_progressive) {
385  }
386 
387  for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) {
388  pcmk_node_t *node = (pcmk_node_t *) iter->data;
389  int health = pe__sum_node_health_scores(node, base_health);
390 
391  // An overall health score of 0 has no effect
392  if (health == 0) {
393  continue;
394  }
395  crm_info("Overall system health of %s is %d",
396  pcmk__node_name(node), health);
397 
398  // Use node health as a location score for each resource on the node
399  for (GList *r = scheduler->priv->resources; r != NULL; r = r->next) {
400  pcmk_resource_t *rsc = (pcmk_resource_t *) r->data;
401 
402  bool constrain = true;
403 
404  if (health < 0) {
405  /* Negative health scores do not apply to resources with
406  * PCMK_META_ALLOW_UNHEALTHY_NODES=true.
407  */
408  constrain = !crm_is_true(g_hash_table_lookup(rsc->priv->meta,
410  }
411  if (constrain) {
412  pcmk__new_location(strategy_str, rsc, health, NULL, node);
413  } else {
414  pcmk__rsc_trace(rsc, "%s is immune from health ban on %s",
415  rsc->id, pcmk__node_name(node));
416  }
417  }
418  }
419 }
420 
431 pcmk_node_t *
433 {
434  GHashTable *allowed_nodes = NULL;
435 
436  if ((rsc == NULL) || (node == NULL)) {
437  return NULL;
438  }
439 
440  if (rsc->priv->parent == NULL) {
441  allowed_nodes = rsc->priv->allowed_nodes;
442  } else {
443  allowed_nodes = rsc->priv->parent->priv->allowed_nodes;
444  }
445  return g_hash_table_lookup(allowed_nodes, node->priv->id);
446 }
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition: utils.c:91
char data[0]
Definition: cpg.c:58
int pcmk__health_score(const char *option, const pcmk_scheduler_t *scheduler)
Definition: health.c:82
pcmk_resource_t * parent
#define pcmk__rsc_trace(rsc, fmt, args...)
void pcmk__copy_node_tables(const pcmk_resource_t *rsc, GHashTable **copy)
pcmk__scheduler_private_t * priv
Definition: scheduler.h:99
pcmk_node_t *(* location)(const pcmk_resource_t *rsc, GList **list, uint32_t target)
High Level API.
pcmk_node_t node2
gboolean shutdown
Definition: nodes.h:62
gboolean unclean
Definition: nodes.h:58
GList * pcmk__sort_nodes(GList *nodes, pcmk_node_t *active_node)
const pcmk__rsc_methods_t * fns
enum pcmk__health_strategy pcmk__parse_health_strategy(const char *value)
Definition: health.c:46
const char * pcmk__cluster_option(GHashTable *options, const char *name)
Definition: options.c:1400
void pcmk__free_node_copy(void *data)
Definition: nodes.c:22
bool pcmk__any_node_available(GHashTable *nodes)
#define PCMK_META_ALLOW_UNHEALTHY_NODES
Definition: options.h:81
pcmk__node_private_t * priv
Definition: nodes.h:85
#define PCMK_VALUE_MINIMAL
Definition: options.h:173
#define crm_trace(fmt, args...)
Definition: logging.h:372
GList * pcmk__copy_node_list(const GList *list, bool reset)
gboolean maintenance
Definition: nodes.h:66
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
pcmk__resource_private_t * priv
Definition: resources.h:61
Wrappers for and extensions to libxml2.
pcmk_scheduler_t * scheduler
void pcmk__apply_node_health(pcmk_scheduler_t *scheduler)
G_GNUC_INTERNAL int pcmk__compare_node_capacities(const pcmk_node_t *node1, const pcmk_node_t *node2)
#define pcmk__assert(expr)
#define PCMK_OPT_NODE_HEALTH_STRATEGY
Definition: options.h:51
G_GNUC_INTERNAL pcmk__location_t * pcmk__new_location(const char *id, pcmk_resource_t *rsc, int node_score, const char *discover_mode, pcmk_node_t *foo_node)
GHashTable * pcmk__copy_node_table(GHashTable *nodes)
GList * nodes
Definition: scheduler.h:97
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:685
pcmk__action_result_t result
Definition: pcmk_fence.c:37
#define PCMK_OPT_NODE_HEALTH_BASE
Definition: options.h:48
pcmk_scheduler_t * scheduler
pcmk_resource_t * remote
void pcmk__restore_node_tables(pcmk_resource_t *rsc, GHashTable *backup)
pcmk_resource_t * launcher
pcmk_node_t * pcmk__top_allowed_node(const pcmk_resource_t *rsc, const pcmk_node_t *node)
pcmk__health_strategy
gboolean crm_is_true(const char *s)
Definition: strings.c:490
#define PCMK_VALUE_BALANCED
Definition: options.h:134
gboolean online
Definition: nodes.h:50
bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
int pe__sum_node_health_scores(const pcmk_node_t *node, int base_health)
Definition: pe_health.c:110
struct pcmk__node_details * details
Definition: nodes.h:82
pcmk_node_t node1
#define crm_info(fmt, args...)
Definition: logging.h:367
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:26
struct pcmk__node_assignment * assign
Definition: nodes.h:79