pacemaker 2.1.6-6fdc9deea29
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-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
15
16// Name for a pseudo-op to use in ordering constraints for utilization
17#define LOAD_STOPPED "load_stopped"
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 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
48struct compare_data {
49 const pe_node_t *node1;
50 const pe_node_t *node2;
51 bool node2_only;
52 int result;
53};
54
67static void
68compare_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
104int
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
131struct calculate_data {
132 GHashTable *current_utilization;
133 bool plus;
134};
135
144static void
145update_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
168void
169pcmk__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
187void
188pcmk__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
204struct capacity_data {
205 const pe_node_t *node;
206 const char *rsc_id;
207 bool is_enough;
208};
209
218static void
219check_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
250static bool
251have_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
275static GHashTable *
276sum_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
297const 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",
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
397static pe_action_t *
398new_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
419void
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
457void
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;
466
467 out->message(out, "node-capacity", node, desc);
468 }
469}
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
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
char data[0]
Definition cpg.c:10
#define INFINITY
Definition crm.h:99
#define RSC_MIGRATE
Definition crm.h:196
G_GNUC_INTERNAL bool pcmk__node_available(const pe_node_t *node, bool consider_score, bool consider_guest)
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)
#define CRM_CHECK(expr, failure_action)
Definition logging.h:235
#define crm_debug(fmt, args...)
Definition logging.h:380
pe_working_set_t * data_set
pcmk__action_result_t result
Definition pcmk_fence.c:35
void pcmk__show_node_capacities(const char *desc, pe_working_set_t *data_set)
void pcmk__release_node_capacity(GHashTable *current_utilization, const pe_resource_t *rsc)
void pcmk__consume_node_capacity(GHashTable *current_utilization, const pe_resource_t *rsc)
#define LOAD_STOPPED
int pcmk__compare_node_capacities(const pe_node_t *node1, const pe_node_t *node2)
const pe_node_t * pcmk__ban_insufficient_capacity(pe_resource_t *rsc)
void pcmk__create_utilization_constraints(pe_resource_t *rsc, const GList *allowed_nodes)
pe_node_t node1
pe_node_t node2
@ pe_order_load
Definition pe_types.h:539
@ pe_action_optional
Definition pe_types.h:319
#define pe_flag_show_utilization
Definition pe_types.h:151
#define pe__show_node_weights(level, rsc, text, nodes, data_set)
Definition internal.h:385
#define start_key(rsc)
Definition internal.h:411
pe_action_t * get_pseudo_op(const char *name, pe_working_set_t *data_set)
Definition pe_actions.c:979
#define stop_key(rsc)
Definition internal.h:405
#define pe_rsc_debug(rsc, fmt, args...)
Definition internal.h:49
#define pe_warn(fmt...)
Definition internal.h:57
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:50
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 pe__clear_action_flags(action, flags_to_clear)
Definition internal.h:98
pe_node_t * pe__copy_node(const pe_node_t *this_node)
Definition utils.c:89
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:611
@ pcmk__str_casei
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
pe_node_t * node
Definition pe_types.h:434
struct pe_node_shared_s * details
Definition pe_types.h:268
const char * uname
Definition pe_types.h:232
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
GHashTable * utilization
Definition pe_types.h:407
GHashTable * allowed_nodes
Definition pe_types.h:400
resource_alloc_functions_t * cmds
Definition pe_types.h:359
const char * placement_strategy
Definition pe_types.h:167
unsigned long long flags
Definition pe_types.h:169
GList *(* colocated_resources)(const pe_resource_t *rsc, const pe_resource_t *orig_rsc, GList *colocated_rscs)