pacemaker  2.1.8-3980678f03
Scalable High-Availability cluster resource manager
pcmk_sched_utilization.c
Go to the documentation of this file.
1 /*
2  * Copyright 2014-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>
12 #include <pacemaker-internal.h>
13 
14 #include "libpacemaker_private.h"
15 
28 static int
29 utilization_value(const char *s)
30 {
31  int value = 0;
32 
33  if ((s != NULL) && (pcmk__scan_min_int(s, &value, INT_MIN) == EINVAL)) {
34  pcmk__config_warn("Using 0 for utilization instead of "
35  "invalid value '%s'", s);
36  value = 0;
37  }
38  return value;
39 }
40 
41 
42 /*
43  * Functions for comparing node capacities
44  */
45 
46 struct compare_data {
47  const pcmk_node_t *node1;
48  const pcmk_node_t *node2;
49  bool node2_only;
50  int result;
51 };
52 
65 static void
66 compare_utilization_value(gpointer key, gpointer value, gpointer user_data)
67 {
68  int node1_capacity = 0;
69  int node2_capacity = 0;
70  struct compare_data *data = user_data;
71  const char *node2_value = NULL;
72 
73  if (data->node2_only) {
74  if (g_hash_table_lookup(data->node1->details->utilization, key)) {
75  return; // We've already compared this attribute
76  }
77  } else {
78  node1_capacity = utilization_value((const char *) value);
79  }
80 
81  node2_value = g_hash_table_lookup(data->node2->details->utilization, key);
82  node2_capacity = utilization_value(node2_value);
83 
84  if (node1_capacity > node2_capacity) {
85  data->result--;
86  } else if (node1_capacity < node2_capacity) {
87  data->result++;
88  }
89 }
90 
102 int
104  const pcmk_node_t *node2)
105 {
106  struct compare_data data = {
107  .node1 = node1,
108  .node2 = node2,
109  .node2_only = false,
110  .result = 0,
111  };
112 
113  // Compare utilization values that node1 and maybe node2 have
114  g_hash_table_foreach(node1->details->utilization, compare_utilization_value,
115  &data);
116 
117  // Compare utilization values that only node2 has
118  data.node2_only = true;
119  g_hash_table_foreach(node2->details->utilization, compare_utilization_value,
120  &data);
121 
122  return data.result;
123 }
124 
125 
126 /*
127  * Functions for updating node capacities
128  */
129 
130 struct calculate_data {
131  GHashTable *current_utilization;
132  bool plus;
133 };
134 
143 static void
144 update_utilization_value(gpointer key, gpointer value, gpointer user_data)
145 {
146  int result = 0;
147  const char *current = NULL;
148  struct calculate_data *data = user_data;
149 
150  current = g_hash_table_lookup(data->current_utilization, key);
151  if (data->plus) {
152  result = utilization_value(current) + utilization_value(value);
153  } else if (current) {
154  result = utilization_value(current) - utilization_value(value);
155  }
156  g_hash_table_replace(data->current_utilization,
157  strdup(key), pcmk__itoa(result));
158 }
159 
167 void
168 pcmk__consume_node_capacity(GHashTable *current_utilization,
169  const pcmk_resource_t *rsc)
170 {
171  struct calculate_data data = {
172  .current_utilization = current_utilization,
173  .plus = false,
174  };
175 
176  g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
177 }
178 
186 void
187 pcmk__release_node_capacity(GHashTable *current_utilization,
188  const pcmk_resource_t *rsc)
189 {
190  struct calculate_data data = {
191  .current_utilization = current_utilization,
192  .plus = true,
193  };
194 
195  g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
196 }
197 
198 
199 /*
200  * Functions for checking for sufficient node capacity
201  */
202 
203 struct capacity_data {
204  const pcmk_node_t *node;
205  const char *rsc_id;
206  bool is_enough;
207 };
208 
217 static void
218 check_capacity(gpointer key, gpointer value, gpointer user_data)
219 {
220  int required = 0;
221  int remaining = 0;
222  const char *node_value_s = NULL;
223  struct capacity_data *data = user_data;
224 
225  node_value_s = g_hash_table_lookup(data->node->details->utilization, key);
226 
227  required = utilization_value(value);
228  remaining = utilization_value(node_value_s);
229 
230  if (required > remaining) {
231  crm_debug("Remaining capacity for %s on %s (%d) is insufficient "
232  "for resource %s usage (%d)",
233  (const char *) key, pcmk__node_name(data->node), remaining,
234  data->rsc_id, required);
235  data->is_enough = false;
236  }
237 }
238 
249 static bool
250 have_enough_capacity(const pcmk_node_t *node, const char *rsc_id,
251  GHashTable *utilization)
252 {
253  struct capacity_data data = {
254  .node = node,
255  .rsc_id = rsc_id,
256  .is_enough = true,
257  };
258 
259  g_hash_table_foreach(utilization, check_capacity, &data);
260  return data.is_enough;
261 }
262 
274 static GHashTable *
275 sum_resource_utilization(const pcmk_resource_t *orig_rsc, GList *rscs)
276 {
277  GHashTable *utilization = pcmk__strkey_table(free, free);
278 
279  for (GList *iter = rscs; iter != NULL; iter = iter->next) {
280  pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
281 
282  rsc->cmds->add_utilization(rsc, orig_rsc, rscs, utilization);
283  }
284  return utilization;
285 }
286 
296 const pcmk_node_t *
298 {
299  bool any_capable = false;
300  char *rscs_id = NULL;
301  pcmk_node_t *node = NULL;
302  const pcmk_node_t *most_capable_node = NULL;
303  GList *colocated_rscs = NULL;
304  GHashTable *unassigned_utilization = NULL;
305  GHashTableIter iter;
306 
307  CRM_CHECK(rsc != NULL, return NULL);
308 
309  // The default placement strategy ignores utilization
310  if (pcmk__str_eq(rsc->cluster->placement_strategy, PCMK_VALUE_DEFAULT,
311  pcmk__str_casei)) {
312  return NULL;
313  }
314 
315  // Check whether any resources are colocated with this one
316  colocated_rscs = rsc->cmds->colocated_resources(rsc, NULL, NULL);
317  if (colocated_rscs == NULL) {
318  return NULL;
319  }
320 
321  rscs_id = crm_strdup_printf("%s and its colocated resources", rsc->id);
322 
323  // If rsc isn't in the list, add it so we include its utilization
324  if (g_list_find(colocated_rscs, rsc) == NULL) {
325  colocated_rscs = g_list_append(colocated_rscs, rsc);
326  }
327 
328  // Sum utilization of colocated resources that haven't been assigned yet
329  unassigned_utilization = sum_resource_utilization(rsc, colocated_rscs);
330 
331  // Check whether any node has enough capacity for all the resources
332  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
333  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
334  if (!pcmk__node_available(node, true, false)) {
335  continue;
336  }
337 
338  if (have_enough_capacity(node, rscs_id, unassigned_utilization)) {
339  any_capable = true;
340  }
341 
342  // Keep track of node with most free capacity
343  if ((most_capable_node == NULL)
344  || (pcmk__compare_node_capacities(node, most_capable_node) < 0)) {
345  most_capable_node = node;
346  }
347  }
348 
349  if (any_capable) {
350  // If so, ban resource from any node with insufficient capacity
351  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
352  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
353  if (pcmk__node_available(node, true, false)
354  && !have_enough_capacity(node, rscs_id,
355  unassigned_utilization)) {
356  pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s",
357  pcmk__node_name(node), rscs_id);
359  "__limit_utilization__", rsc->cluster);
360  }
361  }
362  most_capable_node = NULL;
363 
364  } else {
365  // Otherwise, ban from nodes with insufficient capacity for rsc alone
366  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
367  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
368  if (pcmk__node_available(node, true, false)
369  && !have_enough_capacity(node, rsc->id, rsc->utilization)) {
370  pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s",
371  pcmk__node_name(node), rsc->id);
373  "__limit_utilization__", rsc->cluster);
374  }
375  }
376  }
377 
378  g_hash_table_destroy(unassigned_utilization);
379  g_list_free(colocated_rscs);
380  free(rscs_id);
381 
382  pe__show_node_scores(true, rsc, "Post-utilization", rsc->allowed_nodes,
383  rsc->cluster);
384  return most_capable_node;
385 }
386 
395 static pcmk_action_t *
396 new_load_stopped_op(pcmk_node_t *node)
397 {
398  char *load_stopped_task = crm_strdup_printf(PCMK_ACTION_LOAD_STOPPED "_%s",
399  node->details->uname);
400  pcmk_action_t *load_stopped = get_pseudo_op(load_stopped_task,
401  node->details->data_set);
402 
403  if (load_stopped->node == NULL) {
404  load_stopped->node = pe__copy_node(node);
406  }
407  free(load_stopped_task);
408  return load_stopped;
409 }
410 
418 void
420  const GList *allowed_nodes)
421 {
422  const GList *iter = NULL;
423  pcmk_action_t *load_stopped = NULL;
424 
425  pcmk__rsc_trace(rsc,
426  "Creating utilization constraints for %s - strategy: %s",
427  rsc->id, rsc->cluster->placement_strategy);
428 
429  // "stop rsc then load_stopped" constraints for current nodes
430  for (iter = rsc->running_on; iter != NULL; iter = iter->next) {
431  load_stopped = new_load_stopped_op(iter->data);
432  pcmk__new_ordering(rsc, stop_key(rsc), NULL, NULL, NULL, load_stopped,
434  }
435 
436  // "load_stopped then start/migrate_to rsc" constraints for allowed nodes
437  for (iter = allowed_nodes; iter; iter = iter->next) {
438  load_stopped = new_load_stopped_op(iter->data);
439  pcmk__new_ordering(NULL, NULL, load_stopped, rsc, start_key(rsc), NULL,
441  pcmk__new_ordering(NULL, NULL, load_stopped,
442  rsc,
444  NULL,
446  }
447 }
448 
456 void
458 {
460  return;
461  }
462  for (const GList *iter = scheduler->nodes;
463  iter != NULL; iter = iter->next) {
464  const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
465  pcmk__output_t *out = scheduler->priv;
466 
467  out->message(out, "node-capacity", node, desc);
468  }
469 }
pcmk_assignment_methods_t * cmds
Definition: resources.h:413
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:245
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition: utils.c:89
pcmk_scheduler_t * cluster
Definition: resources.h:408
Actions are ordered if on same node (or migration target for migrate_to)
char data[0]
Definition: cpg.c:58
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
Definition: pe_actions.c:1177
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
G_GNUC_INTERNAL void pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_task, pcmk_action_t *first_action, pcmk_resource_t *then_rsc, char *then_task, pcmk_action_t *then_action, uint32_t flags, pcmk_scheduler_t *sched)
void pcmk__consume_node_capacity(GHashTable *current_utilization, const pcmk_resource_t *rsc)
#define pcmk__config_warn(fmt...)
#define pcmk__rsc_trace(rsc, fmt, args...)
void pcmk__show_node_capacities(const char *desc, pcmk_scheduler_t *scheduler)
const pcmk_node_t * pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc)
#define PCMK_VALUE_DEFAULT
Definition: options.h:142
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:59
pcmk_node_t node2
pcmk_scheduler_t * data_set
Definition: nodes.h:153
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
#define pcmk__rsc_debug(rsc, fmt, args...)
void pcmk__create_utilization_constraints(pcmk_resource_t *rsc, const GList *allowed_nodes)
pcmk_node_t * node
Definition: actions.h:341
#define crm_debug(fmt, args...)
Definition: logging.h:402
#define pcmk__clear_action_flags(action, flags_to_clear)
void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler)
Definition: utils.c:359
#define stop_key(rsc)
Definition: internal.h:213
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:98
struct pe_node_shared_s * details
Definition: nodes.h:167
const char * uname
Definition: nodes.h:73
Wrappers for and extensions to libxml2.
GHashTable * utilization
Definition: resources.h:469
GList *(* colocated_resources)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs)
char * pcmk__op_key(const char *rsc_id, const char *op_type, guint interval_ms)
Generate an operation key (RESOURCE_ACTION_INTERVAL)
Definition: actions.c:196
const char * placement_strategy
Definition: scheduler.h:206
#define PCMK_ACTION_LOAD_STOPPED
Definition: actions.h:54
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:683
pcmk__action_result_t result
Definition: pcmk_fence.c:35
int pcmk__compare_node_capacities(const pcmk_node_t *node1, const pcmk_node_t *node2)
pcmk_scheduler_t * scheduler
void pcmk__release_node_capacity(GHashTable *current_utilization, const pcmk_resource_t *rsc)
This structure contains everything that makes up a single output formatter.
GHashTable * utilization
Definition: nodes.h:143
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition: internal.h:176
GList * running_on
Definition: resources.h:456
#define start_key(rsc)
Definition: internal.h:219
unsigned long long flags
Definition: scheduler.h:211
pcmk_node_t node1
void(* add_utilization)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:24
GHashTable * allowed_nodes
Definition: resources.h:462