This source file includes following definitions.
- utilization_value
- compare_utilization_value
- pcmk__compare_node_capacities
- update_utilization_value
- pcmk__consume_node_capacity
- pcmk__release_node_capacity
- check_capacity
- have_enough_capacity
- sum_resource_utilization
- pcmk__ban_insufficient_capacity
- new_load_stopped_op
- pcmk__create_utilization_constraints
- pcmk__show_node_capacities
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <limits.h>
13
14 #include <crm/common/xml.h>
15 #include <pacemaker-internal.h>
16
17 #include "libpacemaker_private.h"
18
19
20
21
22
23
24
25
26
27
28
29
30
31 static int
32 utilization_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
47
48
49 struct compare_data {
50 const pcmk_node_t *node1;
51 const pcmk_node_t *node2;
52 bool node2_only;
53 int result;
54 };
55
56
57
58
59
60
61
62
63
64
65
66
67
68 static void
69 compare_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->details->utilization, key)) {
78 return;
79 }
80 } else {
81 node1_capacity = utilization_value((const char *) value);
82 }
83
84 node2_value = g_hash_table_lookup(data->node2->details->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
94
95
96
97
98
99
100
101
102
103
104
105 int
106 pcmk__compare_node_capacities(const pcmk_node_t *node1,
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
117 g_hash_table_foreach(node1->details->utilization, compare_utilization_value,
118 &data);
119
120
121 data.node2_only = true;
122 g_hash_table_foreach(node2->details->utilization, compare_utilization_value,
123 &data);
124
125 return data.result;
126 }
127
128
129
130
131
132
133 struct calculate_data {
134 GHashTable *current_utilization;
135 bool plus;
136 };
137
138
139
140
141
142
143
144
145
146 static void
147 update_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
163
164
165
166
167
168
169
170 void
171 pcmk__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->utilization, update_utilization_value, &data);
180 }
181
182
183
184
185
186
187
188
189 void
190 pcmk__release_node_capacity(GHashTable *current_utilization,
191 const pcmk_resource_t *rsc)
192 {
193 struct calculate_data data = {
194 .current_utilization = current_utilization,
195 .plus = true,
196 };
197
198 g_hash_table_foreach(rsc->utilization, update_utilization_value, &data);
199 }
200
201
202
203
204
205
206 struct capacity_data {
207 const pcmk_node_t *node;
208 const char *rsc_id;
209 bool is_enough;
210 };
211
212
213
214
215
216
217
218
219
220 static void
221 check_capacity(gpointer key, gpointer value, gpointer user_data)
222 {
223 int required = 0;
224 int remaining = 0;
225 const char *node_value_s = NULL;
226 struct capacity_data *data = user_data;
227
228 node_value_s = g_hash_table_lookup(data->node->details->utilization, key);
229
230 required = utilization_value(value);
231 remaining = utilization_value(node_value_s);
232
233 if (required > remaining) {
234 crm_debug("Remaining capacity for %s on %s (%d) is insufficient "
235 "for resource %s usage (%d)",
236 (const char *) key, pcmk__node_name(data->node), remaining,
237 data->rsc_id, required);
238 data->is_enough = false;
239 }
240 }
241
242
243
244
245
246
247
248
249
250
251
252 static bool
253 have_enough_capacity(const pcmk_node_t *node, const char *rsc_id,
254 GHashTable *utilization)
255 {
256 struct capacity_data data = {
257 .node = node,
258 .rsc_id = rsc_id,
259 .is_enough = true,
260 };
261
262 g_hash_table_foreach(utilization, check_capacity, &data);
263 return data.is_enough;
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277 static GHashTable *
278 sum_resource_utilization(const pcmk_resource_t *orig_rsc, GList *rscs)
279 {
280 GHashTable *utilization = pcmk__strkey_table(free, free);
281
282 for (GList *iter = rscs; iter != NULL; iter = iter->next) {
283 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
284
285 rsc->cmds->add_utilization(rsc, orig_rsc, rscs, utilization);
286 }
287 return utilization;
288 }
289
290
291
292
293
294
295
296
297
298
299 const pcmk_node_t *
300 pcmk__ban_insufficient_capacity(pcmk_resource_t *rsc)
301 {
302 bool any_capable = false;
303 char *rscs_id = NULL;
304 pcmk_node_t *node = NULL;
305 const pcmk_node_t *most_capable_node = NULL;
306 GList *colocated_rscs = NULL;
307 GHashTable *unassigned_utilization = NULL;
308 GHashTableIter iter;
309
310 CRM_CHECK(rsc != NULL, return NULL);
311
312
313 if (pcmk__str_eq(rsc->cluster->placement_strategy, PCMK_VALUE_DEFAULT,
314 pcmk__str_casei)) {
315 return NULL;
316 }
317
318
319 colocated_rscs = rsc->cmds->colocated_resources(rsc, NULL, NULL);
320 if (colocated_rscs == NULL) {
321 return NULL;
322 }
323
324 rscs_id = crm_strdup_printf("%s and its colocated resources", rsc->id);
325
326
327 if (g_list_find(colocated_rscs, rsc) == NULL) {
328 colocated_rscs = g_list_append(colocated_rscs, rsc);
329 }
330
331
332 unassigned_utilization = sum_resource_utilization(rsc, colocated_rscs);
333
334
335 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
336 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
337 if (!pcmk__node_available(node, true, false)) {
338 continue;
339 }
340
341 if (have_enough_capacity(node, rscs_id, unassigned_utilization)) {
342 any_capable = true;
343 }
344
345
346 if ((most_capable_node == NULL)
347 || (pcmk__compare_node_capacities(node, most_capable_node) < 0)) {
348 most_capable_node = node;
349 }
350 }
351
352 if (any_capable) {
353
354 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
355 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
356 if (pcmk__node_available(node, true, false)
357 && !have_enough_capacity(node, rscs_id,
358 unassigned_utilization)) {
359 pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s",
360 pcmk__node_name(node), rscs_id);
361 resource_location(rsc, node, -PCMK_SCORE_INFINITY,
362 "__limit_utilization__", rsc->cluster);
363 }
364 }
365 most_capable_node = NULL;
366
367 } else {
368
369 g_hash_table_iter_init(&iter, rsc->allowed_nodes);
370 while (g_hash_table_iter_next(&iter, NULL, (void **) &node)) {
371 if (pcmk__node_available(node, true, false)
372 && !have_enough_capacity(node, rsc->id, rsc->utilization)) {
373 pcmk__rsc_debug(rsc, "%s does not have enough capacity for %s",
374 pcmk__node_name(node), rsc->id);
375 resource_location(rsc, node, -PCMK_SCORE_INFINITY,
376 "__limit_utilization__", rsc->cluster);
377 }
378 }
379 }
380
381 g_hash_table_destroy(unassigned_utilization);
382 g_list_free(colocated_rscs);
383 free(rscs_id);
384
385 pe__show_node_scores(true, rsc, "Post-utilization", rsc->allowed_nodes,
386 rsc->cluster);
387 return most_capable_node;
388 }
389
390
391
392
393
394
395
396
397
398 static pcmk_action_t *
399 new_load_stopped_op(pcmk_node_t *node)
400 {
401 char *load_stopped_task = crm_strdup_printf(PCMK_ACTION_LOAD_STOPPED "_%s",
402 node->details->uname);
403 pcmk_action_t *load_stopped = get_pseudo_op(load_stopped_task,
404 node->details->data_set);
405
406 if (load_stopped->node == NULL) {
407 load_stopped->node = pe__copy_node(node);
408 pcmk__clear_action_flags(load_stopped, pcmk_action_optional);
409 }
410 free(load_stopped_task);
411 return load_stopped;
412 }
413
414
415
416
417
418
419
420
421 void
422 pcmk__create_utilization_constraints(pcmk_resource_t *rsc,
423 const GList *allowed_nodes)
424 {
425 const GList *iter = NULL;
426 pcmk_action_t *load_stopped = NULL;
427
428 pcmk__rsc_trace(rsc,
429 "Creating utilization constraints for %s - strategy: %s",
430 rsc->id, rsc->cluster->placement_strategy);
431
432
433 for (iter = rsc->running_on; iter != NULL; iter = iter->next) {
434 load_stopped = new_load_stopped_op(iter->data);
435 pcmk__new_ordering(rsc, stop_key(rsc), NULL, NULL, NULL, load_stopped,
436 pcmk__ar_if_on_same_node_or_target, rsc->cluster);
437 }
438
439
440 for (iter = allowed_nodes; iter; iter = iter->next) {
441 load_stopped = new_load_stopped_op(iter->data);
442 pcmk__new_ordering(NULL, NULL, load_stopped, rsc, start_key(rsc), NULL,
443 pcmk__ar_if_on_same_node_or_target, rsc->cluster);
444 pcmk__new_ordering(NULL, NULL, load_stopped,
445 rsc,
446 pcmk__op_key(rsc->id, PCMK_ACTION_MIGRATE_TO, 0),
447 NULL,
448 pcmk__ar_if_on_same_node_or_target, rsc->cluster);
449 }
450 }
451
452
453
454
455
456
457
458
459 void
460 pcmk__show_node_capacities(const char *desc, pcmk_scheduler_t *scheduler)
461 {
462 if (!pcmk_is_set(scheduler->flags, pcmk_sched_show_utilization)) {
463 return;
464 }
465 for (const GList *iter = scheduler->nodes;
466 iter != NULL; iter = iter->next) {
467 const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
468 pcmk__output_t *out = scheduler->priv;
469
470 out->message(out, "node-capacity", node, desc);
471 }
472 }