pacemaker  2.1.7-0f7f88312f
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 
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 pcmk_node_t *node1;
47  const pcmk_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  const pcmk_node_t *node2)
104 {
105  struct compare_data data = {
106  .node1 = node1,
107  .node2 = node2,
108  .node2_only = false,
109  .result = 0,
110  };
111 
112  // Compare utilization values that node1 and maybe node2 have
113  g_hash_table_foreach(node1->details->utilization, compare_utilization_value,
114  &data);
115 
116  // Compare utilization values that only node2 has
117  data.node2_only = true;
118  g_hash_table_foreach(node2->details->utilization, compare_utilization_value,
119  &data);
120 
121  return data.result;
122 }
123 
124 
125 /*
126  * Functions for updating node capacities
127  */
128 
129 struct calculate_data {
130  GHashTable *current_utilization;
131  bool plus;
132 };
133 
142 static void
143 update_utilization_value(gpointer key, gpointer value, gpointer user_data)
144 {
145  int result = 0;
146  const char *current = NULL;
147  struct calculate_data *data = user_data;
148 
149  current = g_hash_table_lookup(data->current_utilization, key);
150  if (data->plus) {
151  result = utilization_value(current) + utilization_value(value);
152  } else if (current) {
153  result = utilization_value(current) - utilization_value(value);
154  }
155  g_hash_table_replace(data->current_utilization,
156  strdup(key), pcmk__itoa(result));
157 }
158 
166 void
167 pcmk__consume_node_capacity(GHashTable *current_utilization,
168  const pcmk_resource_t *rsc)
169 {
170  struct calculate_data data = {
171  .current_utilization = current_utilization,
172  .plus = false,
173  };
174 
175  g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
176 }
177 
185 void
186 pcmk__release_node_capacity(GHashTable *current_utilization,
187  const pcmk_resource_t *rsc)
188 {
189  struct calculate_data data = {
190  .current_utilization = current_utilization,
191  .plus = true,
192  };
193 
194  g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
195 }
196 
197 
198 /*
199  * Functions for checking for sufficient node capacity
200  */
201 
202 struct capacity_data {
203  const pcmk_node_t *node;
204  const char *rsc_id;
205  bool is_enough;
206 };
207 
216 static void
217 check_capacity(gpointer key, gpointer value, gpointer user_data)
218 {
219  int required = 0;
220  int remaining = 0;
221  const char *node_value_s = NULL;
222  struct capacity_data *data = user_data;
223 
224  node_value_s = g_hash_table_lookup(data->node->details->utilization, key);
225 
226  required = utilization_value(value);
227  remaining = utilization_value(node_value_s);
228 
229  if (required > remaining) {
230  crm_debug("Remaining capacity for %s on %s (%d) is insufficient "
231  "for resource %s usage (%d)",
232  (const char *) key, pe__node_name(data->node), remaining,
233  data->rsc_id, required);
234  data->is_enough = false;
235  }
236 }
237 
248 static bool
249 have_enough_capacity(const pcmk_node_t *node, const char *rsc_id,
250  GHashTable *utilization)
251 {
252  struct capacity_data data = {
253  .node = node,
254  .rsc_id = rsc_id,
255  .is_enough = true,
256  };
257 
258  g_hash_table_foreach(utilization, check_capacity, &data);
259  return data.is_enough;
260 }
261 
273 static GHashTable *
274 sum_resource_utilization(const pcmk_resource_t *orig_rsc, GList *rscs)
275 {
276  GHashTable *utilization = pcmk__strkey_table(free, free);
277 
278  for (GList *iter = rscs; iter != NULL; iter = iter->next) {
279  pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
280 
281  rsc->cmds->add_utilization(rsc, orig_rsc, rscs, utilization);
282  }
283  return utilization;
284 }
285 
295 const pcmk_node_t *
297 {
298  bool any_capable = false;
299  char *rscs_id = NULL;
300  pcmk_node_t *node = NULL;
301  const pcmk_node_t *most_capable_node = NULL;
302  GList *colocated_rscs = NULL;
303  GHashTable *unassigned_utilization = NULL;
304  GHashTableIter iter;
305 
306  CRM_CHECK(rsc != NULL, return NULL);
307 
308  // The default placement strategy ignores utilization
309  if (pcmk__str_eq(rsc->cluster->placement_strategy, "default",
310  pcmk__str_casei)) {
311  return NULL;
312  }
313 
314  // Check whether any resources are colocated with this one
315  colocated_rscs = rsc->cmds->colocated_resources(rsc, NULL, NULL);
316  if (colocated_rscs == NULL) {
317  return NULL;
318  }
319 
320  rscs_id = crm_strdup_printf("%s and its colocated resources", rsc->id);
321 
322  // If rsc isn't in the list, add it so we include its utilization
323  if (g_list_find(colocated_rscs, rsc) == NULL) {
324  colocated_rscs = g_list_append(colocated_rscs, rsc);
325  }
326 
327  // Sum utilization of colocated resources that haven't been assigned yet
328  unassigned_utilization = sum_resource_utilization(rsc, colocated_rscs);
329 
330  // Check whether any node has enough capacity for all the resources
331  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
332  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
333  if (!pcmk__node_available(node, true, false)) {
334  continue;
335  }
336 
337  if (have_enough_capacity(node, rscs_id, unassigned_utilization)) {
338  any_capable = true;
339  }
340 
341  // Keep track of node with most free capacity
342  if ((most_capable_node == NULL)
343  || (pcmk__compare_node_capacities(node, most_capable_node) < 0)) {
344  most_capable_node = node;
345  }
346  }
347 
348  if (any_capable) {
349  // If so, ban resource from any node with insufficient capacity
350  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
351  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
352  if (pcmk__node_available(node, true, false)
353  && !have_enough_capacity(node, rscs_id,
354  unassigned_utilization)) {
355  pe_rsc_debug(rsc, "%s does not have enough capacity for %s",
356  pe__node_name(node), rscs_id);
357  resource_location(rsc, node, -INFINITY, "__limit_utilization__",
358  rsc->cluster);
359  }
360  }
361  most_capable_node = NULL;
362 
363  } else {
364  // Otherwise, ban from nodes with insufficient capacity for rsc alone
365  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
366  while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
367  if (pcmk__node_available(node, true, false)
368  && !have_enough_capacity(node, rsc->id, rsc->utilization)) {
369  pe_rsc_debug(rsc, "%s does not have enough capacity for %s",
370  pe__node_name(node), rsc->id);
371  resource_location(rsc, node, -INFINITY, "__limit_utilization__",
372  rsc->cluster);
373  }
374  }
375  }
376 
377  g_hash_table_destroy(unassigned_utilization);
378  g_list_free(colocated_rscs);
379  free(rscs_id);
380 
381  pe__show_node_scores(true, rsc, "Post-utilization", rsc->allowed_nodes,
382  rsc->cluster);
383  return most_capable_node;
384 }
385 
394 static pcmk_action_t *
395 new_load_stopped_op(pcmk_node_t *node)
396 {
397  char *load_stopped_task = crm_strdup_printf(PCMK_ACTION_LOAD_STOPPED "_%s",
398  node->details->uname);
399  pcmk_action_t *load_stopped = get_pseudo_op(load_stopped_task,
400  node->details->data_set);
401 
402  if (load_stopped->node == NULL) {
403  load_stopped->node = pe__copy_node(node);
405  }
406  free(load_stopped_task);
407  return load_stopped;
408 }
409 
417 void
419  const GList *allowed_nodes)
420 {
421  const GList *iter = NULL;
422  pcmk_action_t *load_stopped = NULL;
423 
424  pe_rsc_trace(rsc, "Creating utilization constraints for %s - strategy: %s",
425  rsc->id, rsc->cluster->placement_strategy);
426 
427  // "stop rsc then load_stopped" constraints for current nodes
428  for (iter = rsc->running_on; iter != NULL; iter = iter->next) {
429  load_stopped = new_load_stopped_op(iter->data);
430  pcmk__new_ordering(rsc, stop_key(rsc), NULL, NULL, NULL, load_stopped,
432  }
433 
434  // "load_stopped then start/migrate_to rsc" constraints for allowed nodes
435  for (iter = allowed_nodes; iter; iter = iter->next) {
436  load_stopped = new_load_stopped_op(iter->data);
437  pcmk__new_ordering(NULL, NULL, load_stopped, rsc, start_key(rsc), NULL,
439  pcmk__new_ordering(NULL, NULL, load_stopped,
440  rsc,
442  NULL,
444  }
445 }
446 
454 void
456 {
458  return;
459  }
460  for (const GList *iter = scheduler->nodes;
461  iter != NULL; iter = iter->next) {
462  const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
463  pcmk__output_t *out = scheduler->priv;
464 
465  out->message(out, "node-capacity", node, desc);
466  }
467 }
pcmk_assignment_methods_t * cmds
Resource assignment methods.
Definition: resources.h:417
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:238
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition: utils.c:89
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition: resources.h:412
Actions are ordered if on same node (or migration target for migrate_to)
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:36
char data[0]
Definition: cpg.c:55
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
Definition: pe_actions.c:1160
#define INFINITY
Definition: crm.h:98
Whether action should not be executed.
Definition: actions.h:244
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)
void pcmk__show_node_capacities(const char *desc, pcmk_scheduler_t *scheduler)
Implementation of pcmk_action_t.
Definition: actions.h:390
const pcmk_node_t * pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc)
#define PCMK_ACTION_MIGRATE_TO
Definition: actions.h:58
pcmk_node_t node2
pcmk_scheduler_t * data_set
Cluster that node is part of.
Definition: nodes.h:126
Implementation of pcmk_scheduler_t.
Definition: scheduler.h:172
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
GList * nodes
Nodes in cluster.
Definition: scheduler.h:195
Whether to show node and resource utilization (in log or output)
Definition: scheduler.h:161
void pcmk__create_utilization_constraints(pcmk_resource_t *rsc, const GList *allowed_nodes)
#define pe_warn(fmt...)
Definition: internal.h:44
pcmk_node_t * node
Node to execute action on, if any.
Definition: actions.h:401
Implementation of pcmk_resource_t.
Definition: resources.h:399
#define crm_debug(fmt, args...)
Definition: logging.h:386
void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler)
Definition: utils.c:360
#define stop_key(rsc)
Definition: internal.h:378
#define pe__clear_action_flags(action, flags_to_clear)
Definition: internal.h:85
void * priv
For Pacemaker use only.
Definition: scheduler.h:229
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:99
struct pe_node_shared_s * details
Basic node information.
Definition: nodes.h:134
const char * uname
Node name in cluster.
Definition: nodes.h:68
GHashTable * utilization
Resource&#39;s utilization attributes.
Definition: resources.h:473
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:42
Implementation of pcmk_node_t.
Definition: nodes.h:130
const char * placement_strategy
Value of placement-strategy property.
Definition: scheduler.h:180
#define PCMK_ACTION_LOAD_STOPPED
Definition: actions.h:54
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition: strings.c:608
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
Node utilization attributes.
Definition: nodes.h:116
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition: internal.h:341
GList * running_on
Nodes where resource may be active.
Definition: resources.h:460
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:37
#define start_key(rsc)
Definition: internal.h:384
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition: scheduler.h:183
pcmk_node_t node1
void(* add_utilization)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization)
char * id
Resource ID in configuration.
Definition: resources.h:400
GHashTable * allowed_nodes
Nodes where resource may run (key is node ID, not name)
Definition: resources.h:466