pacemaker 2.1.7-2.1.7
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
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 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
45struct compare_data {
46 const pcmk_node_t *node1;
47 const pcmk_node_t *node2;
48 bool node2_only;
49 int result;
50};
51
64static void
65compare_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
101int
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
129struct calculate_data {
130 GHashTable *current_utilization;
131 bool plus;
132};
133
142static void
143update_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
166void
167pcmk__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
185void
186pcmk__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
202struct capacity_data {
203 const pcmk_node_t *node;
204 const char *rsc_id;
205 bool is_enough;
206};
207
216static void
217check_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
248static bool
249have_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
273static GHashTable *
274sum_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
295const 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",
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
394static pcmk_action_t *
395new_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
417void
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
454void
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;
464
465 out->message(out, "node-capacity", node, desc);
466 }
467}
@ pcmk__ar_if_on_same_node_or_target
Actions are ordered if on same node (or migration target for migrate_to)
@ pcmk_action_optional
Whether action should not be executed.
Definition actions.h:244
#define PCMK_ACTION_MIGRATE_TO
Definition actions.h:58
#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: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:99
char data[0]
Definition cpg.c:10
#define INFINITY
Definition crm.h:98
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:238
#define crm_debug(fmt, args...)
Definition logging.h:384
pcmk_scheduler_t * scheduler
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:341
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 start_key(rsc)
Definition internal.h:384
#define stop_key(rsc)
Definition internal.h:378
#define pe_rsc_debug(rsc, fmt, args...)
Definition internal.h:36
#define pe_warn(fmt...)
Definition internal.h:44
#define pe_rsc_trace(rsc, fmt, args...)
Definition internal.h:37
#define pe__clear_action_flags(action, flags_to_clear)
Definition internal.h:85
@ pcmk_sched_show_utilization
Whether to show node and resource utilization (in log or output)
Definition scheduler.h:161
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:608
@ pcmk__str_casei
This structure contains everything that makes up a single output formatter.
int(* message)(pcmk__output_t *out, const char *message_id,...)
Implementation of pcmk_action_t.
Definition actions.h:390
pcmk_node_t * node
Node to execute action on, if any.
Definition actions.h:401
Implementation of pcmk_node_t.
Definition nodes.h:130
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
Node utilization attributes.
Definition nodes.h:116
pcmk_scheduler_t * data_set
Cluster that node is part of.
Definition nodes.h:126
Implementation of pcmk_resource_t.
Definition resources.h:399
pcmk_assignment_methods_t * cmds
Resource assignment methods.
Definition resources.h:417
GList * running_on
Nodes where resource may be active.
Definition resources.h:460
pcmk_scheduler_t * cluster
Cluster that resource is part of.
Definition resources.h:412
char * id
Resource ID in configuration.
Definition resources.h:400
GHashTable * utilization
Resource's utilization attributes.
Definition resources.h:473
GHashTable * allowed_nodes
Nodes where resource may run (key is node ID, not name)
Definition resources.h:466
Implementation of pcmk_scheduler_t.
Definition scheduler.h:172
const char * placement_strategy
Value of placement-strategy property.
Definition scheduler.h:180
unsigned long long flags
Group of enum pcmk_scheduler_flags.
Definition scheduler.h:183
void * priv
For Pacemaker use only.
Definition scheduler.h:229
GList * nodes
Nodes in cluster.
Definition scheduler.h:195
GList *(* colocated_resources)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs)