pacemaker 3.0.1-16e74fc4da
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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
12#include <limits.h> // INT_MIN, INT_MAX
13
14#include <crm/common/xml.h>
15#include <pacemaker-internal.h>
16
18
31static int
32utilization_value(const char *s)
33{
34 int value = 0;
35
36 if ((s != NULL) && (pcmk__scan_min_int(s, &value, INT_MIN) == EINVAL)) {
37 pcmk__config_warn("Using 0 for utilization instead of "
38 "invalid value '%s'", s);
39 value = 0;
40 }
41 return value;
42}
43
44
45/*
46 * Functions for comparing node capacities
47 */
48
49struct compare_data {
50 const pcmk_node_t *node1;
51 const pcmk_node_t *node2;
52 bool node2_only;
53 int result;
54};
55
68static void
69compare_utilization_value(gpointer key, gpointer value, gpointer user_data)
70{
71 int node1_capacity = 0;
72 int node2_capacity = 0;
73 struct compare_data *data = user_data;
74 const char *node2_value = NULL;
75
76 if (data->node2_only) {
77 if (g_hash_table_lookup(data->node1->priv->utilization, key)) {
78 return; // We've already compared this attribute
79 }
80 } else {
81 node1_capacity = utilization_value((const char *) value);
82 }
83
84 node2_value = g_hash_table_lookup(data->node2->priv->utilization, key);
85 node2_capacity = utilization_value(node2_value);
86
87 if (node1_capacity > node2_capacity) {
88 data->result--;
89 } else if (node1_capacity < node2_capacity) {
90 data->result++;
91 }
92}
93
105int
107 const pcmk_node_t *node2)
108{
109 struct compare_data data = {
110 .node1 = node1,
111 .node2 = node2,
112 .node2_only = false,
113 .result = 0,
114 };
115
116 // Compare utilization values that node1 and maybe node2 have
117 g_hash_table_foreach(node1->priv->utilization, compare_utilization_value,
118 &data);
119
120 // Compare utilization values that only node2 has
121 data.node2_only = true;
122 g_hash_table_foreach(node2->priv->utilization, compare_utilization_value,
123 &data);
124
125 return data.result;
126}
127
128
129/*
130 * Functions for updating node capacities
131 */
132
133struct calculate_data {
134 GHashTable *current_utilization;
135 bool plus;
136};
137
146static void
147update_utilization_value(gpointer key, gpointer value, gpointer user_data)
148{
149 struct calculate_data *data = user_data;
150 const char *current = g_hash_table_lookup(data->current_utilization, key);
151 long long result = utilization_value(current)
152 + (data->plus? 1LL : -1LL) * utilization_value(value);
153
154 if (result < INT_MIN) {
155 result = INT_MIN;
156 } else if (result > INT_MAX) {
157 result = INT_MAX;
158 }
159 g_hash_table_replace(data->current_utilization,
160 strdup(key), pcmk__itoa((int) result));
161}
162
170void
171pcmk__consume_node_capacity(GHashTable *current_utilization,
172 const pcmk_resource_t *rsc)
173{
174 struct calculate_data data = {
175 .current_utilization = current_utilization,
176 .plus = false,
177 };
178
179 g_hash_table_foreach(rsc->priv->utilization, update_utilization_value,
180 &data);
181}
182
190void
191pcmk__release_node_capacity(GHashTable *current_utilization,
192 const pcmk_resource_t *rsc)
193{
194 struct calculate_data data = {
195 .current_utilization = current_utilization,
196 .plus = true,
197 };
198
199 g_hash_table_foreach(rsc->priv->utilization, update_utilization_value,
200 &data);
201}
202
203
204/*
205 * Functions for checking for sufficient node capacity
206 */
207
208struct capacity_data {
209 const pcmk_node_t *node;
210 const char *rsc_id;
211 bool is_enough;
212};
213
222static void
223check_capacity(gpointer key, gpointer value, gpointer user_data)
224{
225 int required = 0;
226 int remaining = 0;
227 const char *node_value_s = NULL;
228 struct capacity_data *data = user_data;
229
230 node_value_s = g_hash_table_lookup(data->node->priv->utilization, key);
231
232 required = utilization_value(value);
233 remaining = utilization_value(node_value_s);
234
235 if (required > remaining) {
236 crm_debug("Remaining capacity for %s on %s (%d) is insufficient "
237 "for resource %s usage (%d)",
238 (const char *) key, pcmk__node_name(data->node), remaining,
239 data->rsc_id, required);
240 data->is_enough = false;
241 }
242}
243
254static bool
255have_enough_capacity(const pcmk_node_t *node, const char *rsc_id,
256 GHashTable *utilization)
257{
258 struct capacity_data data = {
259 .node = node,
260 .rsc_id = rsc_id,
261 .is_enough = true,
262 };
263
264 g_hash_table_foreach(utilization, check_capacity, &data);
265 return data.is_enough;
266}
267
279static GHashTable *
280sum_resource_utilization(const pcmk_resource_t *orig_rsc, GList *rscs)
281{
282 GHashTable *utilization = pcmk__strkey_table(free, free);
283
284 for (GList *iter = rscs; iter != NULL; iter = iter->next) {
285 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
286
287 rsc->priv->cmds->add_utilization(rsc, orig_rsc, rscs, utilization);
288 }
289 return utilization;
290}
291
301const pcmk_node_t *
303{
304 bool any_capable = false;
305 char *rscs_id = NULL;
306 pcmk_node_t *node = NULL;
307 const pcmk_node_t *most_capable_node = NULL;
308 GList *colocated_rscs = NULL;
309 GHashTable *unassigned_utilization = NULL;
310 GHashTableIter iter;
311
312 CRM_CHECK(rsc != NULL, return NULL);
313
314 // The default placement strategy ignores utilization
315 if (pcmk__str_eq(rsc->priv->scheduler->priv->placement_strategy,
317 return NULL;
318 }
319
320 // Check whether any resources are colocated with this one
321 colocated_rscs = rsc->priv->cmds->colocated_resources(rsc, NULL, NULL);
322 if (colocated_rscs == NULL) {
323 return NULL;
324 }
325
326 rscs_id = crm_strdup_printf("%s and its colocated resources", rsc->id);
327
328 // If rsc isn't in the list, add it so we include its utilization
329 if (g_list_find(colocated_rscs, rsc) == NULL) {
330 colocated_rscs = g_list_append(colocated_rscs, rsc);
331 }
332
333 // Sum utilization of colocated resources that haven't been assigned yet
334 unassigned_utilization = sum_resource_utilization(rsc, colocated_rscs);
335
336 // Check whether any node has enough capacity for all the resources
337 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
338 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
339 if (!pcmk__node_available(node, true, false)) {
340 continue;
341 }
342
343 if (have_enough_capacity(node, rscs_id, unassigned_utilization)) {
344 any_capable = true;
345 }
346
347 // Keep track of node with most free capacity
348 if ((most_capable_node == NULL)
349 || (pcmk__compare_node_capacities(node, most_capable_node) < 0)) {
350 most_capable_node = node;
351 }
352 }
353
354 if (any_capable) {
355 // If so, ban resource from any node with insufficient capacity
356 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
357 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
358 if (pcmk__node_available(node, true, false)
359 && !have_enough_capacity(node, rscs_id,
360 unassigned_utilization)) {
361 pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s",
362 pcmk__node_name(node), rscs_id);
364 "__limit_utilization__",
365 rsc->priv->scheduler);
366 }
367 }
368 most_capable_node = NULL;
369
370 } else {
371 // Otherwise, ban from nodes with insufficient capacity for rsc alone
372 g_hash_table_iter_init(&iter, rsc->priv->allowed_nodes);
373 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
374 if (pcmk__node_available(node, true, false)
375 && !have_enough_capacity(node, rsc->id,
376 rsc->priv->utilization)) {
377 pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s",
378 pcmk__node_name(node), rsc->id);
380 "__limit_utilization__",
381 rsc->priv->scheduler);
382 }
383 }
384 }
385
386 g_hash_table_destroy(unassigned_utilization);
387 g_list_free(colocated_rscs);
388 free(rscs_id);
389
390 pe__show_node_scores(true, rsc, "Post-utilization",
391 rsc->priv->allowed_nodes, rsc->priv->scheduler);
392 return most_capable_node;
393}
394
403static pcmk_action_t *
404new_load_stopped_op(pcmk_node_t *node)
405{
406 char *load_stopped_task = crm_strdup_printf(PCMK_ACTION_LOAD_STOPPED "_%s",
407 node->priv->name);
408 pcmk_action_t *load_stopped = get_pseudo_op(load_stopped_task,
409 node->priv->scheduler);
410
411 if (load_stopped->node == NULL) {
412 load_stopped->node = pe__copy_node(node);
414 }
415 free(load_stopped_task);
416 return load_stopped;
417}
418
426void
428 const GList *allowed_nodes)
429{
430 const GList *iter = NULL;
431 pcmk_action_t *load_stopped = NULL;
432
433 pcmk__rsc_trace(rsc,
434 "Creating utilization constraints for %s - strategy: %s",
435 rsc->id, rsc->priv->scheduler->priv->placement_strategy);
436
437 // "stop rsc then load_stopped" constraints for current nodes
438 for (iter = rsc->priv->active_nodes; iter != NULL; iter = iter->next) {
439 load_stopped = new_load_stopped_op(iter->data);
440 pcmk__new_ordering(rsc, stop_key(rsc), NULL, NULL, NULL, load_stopped,
442 rsc->priv->scheduler);
443 }
444
445 // "load_stopped then start/migrate_to rsc" constraints for allowed nodes
446 for (iter = allowed_nodes; iter; iter = iter->next) {
447 load_stopped = new_load_stopped_op(iter->data);
448 pcmk__new_ordering(NULL, NULL, load_stopped, rsc, start_key(rsc), NULL,
450 rsc->priv->scheduler);
451 pcmk__new_ordering(NULL, NULL, load_stopped,
452 rsc,
454 NULL,
456 rsc->priv->scheduler);
457 }
458}
459
467void
469{
471 return;
472 }
473 for (const GList *iter = scheduler->nodes;
474 iter != NULL; iter = iter->next) {
475 const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
477
478 // Utilization doesn't apply to bundle nodes
479 if (pcmk__is_bundle_node(node)) {
480 continue;
481 }
482
483 out->message(out, "node-capacity", node, desc);
484 }
485}
@ pcmk__ar_if_on_same_node_or_target
Actions are ordered if on same node (or migration target for migrate_to)
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:50
#define PCMK_ACTION_LOAD_STOPPED
Definition actions.h:45
@ pcmk__action_optional
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:225
#define pcmk__clear_action_flags(action, flags_to_clear)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition util.h:80
char data[0]
Definition cpg.c:10
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)
G_GNUC_INTERNAL bool pcmk__node_available(const pcmk_node_t *node, bool consider_score, bool consider_guest)
#define CRM_CHECK(expr, failure_action)
Definition logging.h:213
#define crm_debug(fmt, args...)
Definition logging.h:368
#define pcmk__config_warn(fmt...)
pcmk_scheduler_t * scheduler
#define PCMK_VALUE_DEFAULT
Definition options.h:144
pcmk__action_result_t result
Definition pcmk_fence.c:37
void pcmk__show_node_capacities(const char *desc, pcmk_scheduler_t *scheduler)
void pcmk__create_utilization_constraints(pcmk_resource_t *rsc, const GList *allowed_nodes)
const pcmk_node_t * pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc)
int pcmk__compare_node_capacities(const pcmk_node_t *node1, const pcmk_node_t *node2)
void pcmk__release_node_capacity(GHashTable *current_utilization, const pcmk_resource_t *rsc)
void pcmk__consume_node_capacity(GHashTable *current_utilization, const pcmk_resource_t *rsc)
pcmk_node_t node2
pcmk_node_t node1
pcmk_node_t * pe__copy_node(const pcmk_node_t *this_node)
Definition utils.c:124
pcmk_action_t * get_pseudo_op(const char *name, pcmk_scheduler_t *scheduler)
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition internal.h:158
void resource_location(pcmk_resource_t *rsc, const pcmk_node_t *node, int score, const char *tag, pcmk_scheduler_t *scheduler)
Definition utils.c:398
#define start_key(rsc)
Definition internal.h:192
#define stop_key(rsc)
Definition internal.h:190
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__rsc_debug(rsc, fmt, args...)
@ pcmk__sched_show_utilization
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:26
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:116
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:685
@ pcmk__str_casei
pcmk_node_t * node
GList *(* colocated_resources)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs)
GHashTable * utilization
pcmk_scheduler_t * scheduler
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
pcmk_scheduler_t * scheduler
const pcmk__assignment_methods_t * cmds
pcmk__resource_private_t * priv
Definition resources.h:61
pcmk__scheduler_private_t * priv
Definition scheduler.h:99
GList * nodes
Definition scheduler.h:97
uint64_t flags
Definition scheduler.h:89
pcmk__node_private_t * priv
Definition nodes.h:85
Wrappers for and extensions to libxml2.