pacemaker 2.1.8-2.1.8
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#include <crm/common/xml.h>
12#include <pacemaker-internal.h>
13
15
28static int
29utilization_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
46struct compare_data {
47 const pcmk_node_t *node1;
48 const pcmk_node_t *node2;
49 bool node2_only;
50 int result;
51};
52
65static void
66compare_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
102int
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
130struct calculate_data {
131 GHashTable *current_utilization;
132 bool plus;
133};
134
143static void
144update_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
167void
168pcmk__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
186void
187pcmk__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
203struct capacity_data {
204 const pcmk_node_t *node;
205 const char *rsc_id;
206 bool is_enough;
207};
208
217static void
218check_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
249static bool
250have_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
274static GHashTable *
275sum_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
296const 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,
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
395static pcmk_action_t *
396new_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
418void
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
456void
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;
466
467 out->message(out, "node-capacity", node, desc);
468 }
469}
@ pcmk__ar_if_on_same_node_or_target
Actions are ordered if on same node (or migration target for migrate_to)
@ pcmk_action_optional
Definition actions.h:210
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:59
#define PCMK_ACTION_LOAD_STOPPED
Definition actions.h:54
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
#define pcmk__clear_action_flags(action, flags_to_clear)
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
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:245
#define crm_debug(fmt, args...)
Definition logging.h:400
#define pcmk__config_warn(fmt...)
pcmk_scheduler_t * scheduler
#define PCMK_VALUE_DEFAULT
Definition options.h:142
pcmk__action_result_t result
Definition pcmk_fence.c:35
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:89
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:176
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 start_key(rsc)
Definition internal.h:219
#define stop_key(rsc)
Definition internal.h:213
@ pcmk_sched_show_utilization
Definition scheduler.h:176
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__rsc_debug(rsc, fmt, args...)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition scores.h:24
int pcmk__scan_min_int(const char *text, int *result, int minimum)
Definition strings.c:127
GHashTable * pcmk__strkey_table(GDestroyNotify key_destroy_func, GDestroyNotify value_destroy_func)
Definition strings.c:683
@ pcmk__str_casei
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
pcmk_node_t * node
Definition actions.h:341
struct pe_node_shared_s * details
Definition nodes.h:167
const char * uname
Definition nodes.h:73
GHashTable * utilization
Definition nodes.h:143
pcmk_scheduler_t * data_set
Definition nodes.h:153
pcmk_assignment_methods_t * cmds
Definition resources.h:413
GList * running_on
Definition resources.h:456
pcmk_scheduler_t * cluster
Definition resources.h:408
GHashTable * utilization
Definition resources.h:469
GHashTable * allowed_nodes
Definition resources.h:462
const char * placement_strategy
Definition scheduler.h:206
unsigned long long flags
Definition scheduler.h:211
GList *(* colocated_resources)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs)
Wrappers for and extensions to libxml2.