pacemaker  2.1.3-ea053b43a
Scalable High-Availability cluster resource manager
pcmk_sched_utilization.c
Go to the documentation of this file.
1 /*
2  * Copyright 2014-2022 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 
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  pe_warn("Using 0 for utilization instead of invalid value '%s'", value);
35  value = 0;
36  }
37  return value;
38 }
39 
40 
41 /*
42  * Functions for comparing node capacities
43  */
44 
45 struct compare_data {
46  const pe_node_t *node1;
47  const pe_node_t *node2;
48  bool node2_only;
49  int result;
50 };
51 
64 static void
65 compare_utilization_value(gpointer key, gpointer value, gpointer user_data)
66 {
67  int node1_capacity = 0;
68  int node2_capacity = 0;
69  struct compare_data *data = user_data;
70  const char *node2_value = NULL;
71 
72  if (data->node2_only) {
73  if (g_hash_table_lookup(data->node1->details->utilization, key)) {
74  return; // We've already compared this attribute
75  }
76  } else {
77  node1_capacity = utilization_value((const char *) value);
78  }
79 
80  node2_value = g_hash_table_lookup(data->node2->details->utilization, key);
81  node2_capacity = utilization_value(node2_value);
82 
83  if (node1_capacity > node2_capacity) {
84  data->result--;
85  } else if (node1_capacity < node2_capacity) {
86  data->result++;
87  }
88 }
89 
101 int
103 {
104  struct compare_data data = {
105  .node1 = node1,
106  .node2 = node2,
107  .node2_only = false,
108  .result = 0,
109  };
110 
111  // Compare utilization values that node1 and maybe node2 have
112  g_hash_table_foreach(node1->details->utilization, compare_utilization_value,
113  &data);
114 
115  // Compare utilization values that only node2 has
116  data.node2_only = true;
117  g_hash_table_foreach(node2->details->utilization, compare_utilization_value,
118  &data);
119 
120  return data.result;
121 }
122 
123 
124 /*
125  * Functions for updating node capacities
126  */
127 
128 struct calculate_data {
129  GHashTable *current_utilization;
130  bool plus;
131 };
132 
141 static void
142 update_utilization_value(gpointer key, gpointer value, gpointer user_data)
143 {
144  int result = 0;
145  const char *current = NULL;
146  struct calculate_data *data = user_data;
147 
148  current = g_hash_table_lookup(data->current_utilization, key);
149  if (data->plus) {
150  result = utilization_value(current) + utilization_value(value);
151  } else if (current) {
152  result = utilization_value(current) - utilization_value(value);
153  }
154  g_hash_table_replace(data->current_utilization,
155  strdup(key), pcmk__itoa(result));
156 }
157 
165 void
166 pcmk__consume_node_capacity(GHashTable *current_utilization, pe_resource_t *rsc)
167 {
168  struct calculate_data data = {
169  .current_utilization = current_utilization,
170  .plus = false,
171  };
172 
173  g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
174 }
175 
183 void
184 pcmk__release_node_capacity(GHashTable *current_utilization, pe_resource_t *rsc)
185 {
186  struct calculate_data data = {
187  .current_utilization = current_utilization,
188  .plus = true,
189  };
190 
191  g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
192 }
193 
194 
195 /*
196  * Functions for checking for sufficient node capacity
197  */
198 
199 struct capacity_data {
200  pe_node_t *node;
201  const char *rsc_id;
202  bool is_enough;
203 };
204 
213 static void
214 check_capacity(gpointer key, gpointer value, gpointer user_data)
215 {
216  int required = 0;
217  int remaining = 0;
218  const char *node_value_s = NULL;
219  struct capacity_data *data = user_data;
220 
221  node_value_s = g_hash_table_lookup(data->node->details->utilization, key);
222 
223  required = utilization_value(value);
224  remaining = utilization_value(node_value_s);
225 
226  if (required > remaining) {
227  crm_debug("Remaining capacity for %s on %s (%d) is insufficient "
228  "for resource %s usage (%d)",
229  (const char *) key, data->node->details->uname, remaining,
230  data->rsc_id, required);
231  data->is_enough = false;
232  }
233 }
234 
245 static bool
246 have_enough_capacity(pe_node_t *node, const char *rsc_id,
247  GHashTable *utilization)
248 {
249  struct capacity_data data = {
250  .node = node,
251  .rsc_id = rsc_id,
252  .is_enough = true,
253  };
254 
255  g_hash_table_foreach(utilization, check_capacity, &data);
256  return data.is_enough;
257 }
258 
270 static GHashTable *
271 sum_resource_utilization(pe_resource_t *orig_rsc, GList *rscs)
272 {
273  GHashTable *utilization = pcmk__strkey_table(free, free);
274 
275  for (GList *iter = rscs; iter != NULL; iter = iter->next) {
276  pe_resource_t *rsc = (pe_resource_t *) iter->data;
277 
278  rsc->cmds->add_utilization(rsc, orig_rsc, rscs, utilization);
279  }
280  return utilization;
281 }
282 
291 void
294 {
295  bool any_capable = false;
296  char *rscs_id = NULL;
297  pe_node_t *node = NULL;
298  pe_node_t *most_capable_node = NULL;
299  GList *colocated_rscs = NULL;
300  GHashTable *unallocated_utilization = NULL;
301  GHashTableIter iter;
302 
303  CRM_CHECK((rsc != NULL) && (prefer != NULL) && (data_set != NULL), return);
304 
305  // The default placement strategy ignores utilization
306  if (pcmk__str_eq(data_set->placement_strategy, "default",
307  pcmk__str_casei)) {
308  return;
309  }
310 
311  // Check whether any resources are colocated with this one
312  colocated_rscs = rsc->cmds->colocated_resources(rsc, NULL, NULL);
313  if (colocated_rscs == NULL) {
314  return;
315  }
316 
317  rscs_id = crm_strdup_printf("%s and its colocated resources", rsc->id);
318 
319  // If rsc isn't in the list, add it so we include its utilization
320  if (g_list_find(colocated_rscs, rsc) == NULL) {
321  colocated_rscs = g_list_append(colocated_rscs, rsc);
322  }
323 
324  // Sum utilization of colocated resources that haven't been allocated yet
325  unallocated_utilization = sum_resource_utilization(rsc, colocated_rscs);
326 
327  // Check whether any node has enough capacity for all the resources
328  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
329  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
330  if (!pcmk__node_available(node) || (node->weight < 0)) {
331  continue;
332  }
333 
334  if (have_enough_capacity(node, rscs_id, unallocated_utilization)) {
335  any_capable = true;
336  }
337 
338  // Keep track of node with most free capacity
339  if ((most_capable_node == NULL)
340  || (pcmk__compare_node_capacities(node, most_capable_node) < 0)) {
341  most_capable_node = node;
342  }
343  }
344 
345  if (any_capable) {
346  // If so, ban resource from any node with insufficient capacity
347  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
348  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
349  if ((node->weight >= 0) && pcmk__node_available(node)
350  && !have_enough_capacity(node, rscs_id,
351  unallocated_utilization)) {
352  pe_rsc_debug(rsc, "%s does not have enough capacity for %s",
353  node->details->uname, rscs_id);
354  resource_location(rsc, node, -INFINITY, "__limit_utilization__",
355  data_set);
356  }
357  }
358 
359  } else {
360  // Otherwise, ban from nodes with insufficient capacity for rsc alone
361  if (*prefer == NULL) {
362  *prefer = most_capable_node;
363  }
364  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
365  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
366  if ((node->weight >= 0) && pcmk__node_available(node)
367  && !have_enough_capacity(node, rsc->id, rsc->utilization)) {
368  pe_rsc_debug(rsc, "%s does not have enough capacity for %s",
369  node->details->uname, rsc->id);
370  resource_location(rsc, node, -INFINITY, "__limit_utilization__",
371  data_set);
372  }
373  }
374  }
375 
376  g_hash_table_destroy(unallocated_utilization);
377  g_list_free(colocated_rscs);
378  free(rscs_id);
379 
380  pe__show_node_weights(true, rsc, "Post-utilization",
381  rsc->allowed_nodes, data_set);
382 }
383 
393 static pe_action_t *
394 new_load_stopped_op(const pe_node_t *node, pe_working_set_t *data_set)
395 {
396  char *load_stopped_task = crm_strdup_printf(LOAD_STOPPED "_%s",
397  node->details->uname);
398  pe_action_t *load_stopped = get_pseudo_op(load_stopped_task, data_set);
399 
400  if (load_stopped->node == NULL) {
401  load_stopped->node = pe__copy_node(node);
403  }
404  free(load_stopped_task);
405  return load_stopped;
406 }
407 
415 void
417 {
418  GList *iter = NULL;
419  pe_node_t *node = NULL;
420  pe_action_t *load_stopped = NULL;
421 
422  pe_rsc_trace(rsc, "Creating utilization constraints for %s - strategy: %s",
423  rsc->id, rsc->cluster->placement_strategy);
424 
425  // "stop rsc then load_stopped" constraints for current nodes
426  for (iter = rsc->running_on; iter != NULL; iter = iter->next) {
427  node = (pe_node_t *) iter->data;
428  load_stopped = new_load_stopped_op(node, rsc->cluster);
429  pcmk__new_ordering(rsc, stop_key(rsc), NULL, NULL, NULL, load_stopped,
430  pe_order_load, rsc->cluster);
431  }
432 
433  // "load_stopped then start/migrate_to rsc" constraints for allowed nodes
434  for (GList *iter = allowed_nodes; iter; iter = iter->next) {
435  node = (pe_node_t *) iter->data;
436  load_stopped = new_load_stopped_op(node, rsc->cluster);
437  pcmk__new_ordering(NULL, NULL, load_stopped, rsc, start_key(rsc), NULL,
438  pe_order_load, rsc->cluster);
439  pcmk__new_ordering(NULL, NULL, load_stopped,
440  rsc, pcmk__op_key(rsc->id, RSC_MIGRATE, 0), NULL,
441  pe_order_load, rsc->cluster);
442  }
443 }
444 
452 void
454 {
456  return;
457  }
458  for (GList *iter = data_set->nodes; iter != NULL; iter = iter->next) {
459  pe_node_t *node = (pe_node_t *) iter->data;
460  pcmk__output_t *out = data_set->priv;
461 
462  out->message(out, "node-capacity", node, desc);
463  }
464 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:226
void(* add_utilization)(pe_resource_t *rsc, pe_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization)
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:21
char data[0]
Definition: cpg.c:55
#define INFINITY
Definition: crm.h:99
#define LOAD_STOPPED
#define pe__show_node_weights(level, rsc, text, nodes, data_set)
Definition: internal.h:355
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition: strings.c:127
G_GNUC_INTERNAL bool pcmk__node_available(const pe_node_t *node)
resource_alloc_functions_t * cmds
Definition: pe_types.h:348
void pcmk__ban_insufficient_capacity(pe_resource_t *rsc, pe_node_t **prefer, pe_working_set_t *data_set)
void pcmk__release_node_capacity(GHashTable *current_utilization, pe_resource_t *rsc)
G_GNUC_INTERNAL void pcmk__new_ordering(pe_resource_t *lh_rsc, char *lh_task, pe_action_t *lh_action, pe_resource_t *rh_rsc, char *rh_task, pe_action_t *rh_action, enum pe_ordering type, pe_working_set_t *data_set)
pe_node_t * pe__copy_node(const pe_node_t *this_node)
Definition: utils.c:141
void resource_location(pe_resource_t *rsc, pe_node_t *node, int score, const char *tag, pe_working_set_t *data_set)
Definition: utils.c:1692
#define RSC_MIGRATE
Definition: crm.h:198
void pcmk__consume_node_capacity(GHashTable *current_utilization, pe_resource_t *rsc)
GList * nodes
Definition: pe_types.h:164
#define pe_warn(fmt...)
Definition: internal.h:29
pe_action_t * get_pseudo_op(const char *name, pe_working_set_t *data_set)
Definition: utils.c:1953
#define crm_debug(fmt, args...)
Definition: logging.h:363
#define stop_key(rsc)
Definition: internal.h:378
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:70
void pcmk__show_node_capacities(const char *desc, pe_working_set_t *data_set)
GList *(* colocated_resources)(pe_resource_t *rsc, pe_resource_t *orig_rsc, GList *colocated_rscs)
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:122
struct pe_node_shared_s * details
Definition: pe_types.h:252
pe_node_t * node
Definition: pe_types.h:425
const char * uname
Definition: pe_types.h:216
pe_working_set_t * data_set
GHashTable * utilization
Definition: pe_types.h:389
const char * placement_strategy
Definition: pe_types.h:151
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:45
void pcmk__create_utilization_constraints(pe_resource_t *rsc, GList *allowed_nodes)
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:34
#define pe_flag_show_utilization
Definition: pe_types.h:135
This structure contains everything that makes up a single output formatter.
GHashTable * utilization
Definition: pe_types.h:242
GList * running_on
Definition: pe_types.h:380
pe_working_set_t * cluster
Definition: pe_types.h:342
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:22
#define start_key(rsc)
Definition: internal.h:384
unsigned long long flags
Definition: pe_types.h:153
int pcmk__compare_node_capacities(const pe_node_t *node1, const pe_node_t *node2)
char * id
Definition: pe_types.h:336
GHashTable * allowed_nodes
Definition: pe_types.h:382