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