pacemaker  2.0.5-ba59be712
Scalable High-Availability cluster resource manager
pcmk_sched_utilization.c
Go to the documentation of this file.
1 /*
2  * Copyright 2014-2020 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 
14 static GListPtr find_colocated_rscs(GListPtr colocated_rscs, pe_resource_t * rsc,
15  pe_resource_t * orig_rsc);
16 
17 static GListPtr group_find_colocated_rscs(GListPtr colocated_rscs, pe_resource_t * rsc,
18  pe_resource_t * orig_rsc);
19 
20 static void group_add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc,
21  GListPtr all_rscs);
22 
23 struct compare_data {
24  const pe_node_t *node1;
25  const pe_node_t *node2;
26  int result;
27 };
28 
29 static void
30 do_compare_capacity1(gpointer key, gpointer value, gpointer user_data)
31 {
32  int node1_capacity = 0;
33  int node2_capacity = 0;
34  struct compare_data *data = user_data;
35 
36  node1_capacity = crm_parse_int(value, "0");
37  node2_capacity =
38  crm_parse_int(g_hash_table_lookup(data->node2->details->utilization, key), "0");
39 
40  if (node1_capacity > node2_capacity) {
41  data->result--;
42  } else if (node1_capacity < node2_capacity) {
43  data->result++;
44  }
45 }
46 
47 static void
48 do_compare_capacity2(gpointer key, gpointer value, gpointer user_data)
49 {
50  int node1_capacity = 0;
51  int node2_capacity = 0;
52  struct compare_data *data = user_data;
53 
54  if (g_hash_table_lookup_extended(data->node1->details->utilization, key, NULL, NULL)) {
55  return;
56  }
57 
58  node1_capacity = 0;
59  node2_capacity = crm_parse_int(value, "0");
60 
61  if (node1_capacity > node2_capacity) {
62  data->result--;
63  } else if (node1_capacity < node2_capacity) {
64  data->result++;
65  }
66 }
67 
68 /* rc < 0 if 'node1' has more capacity remaining
69  * rc > 0 if 'node1' has less capacity remaining
70  */
71 int
72 compare_capacity(const pe_node_t * node1, const pe_node_t * node2)
73 {
74  struct compare_data data;
75 
76  data.node1 = node1;
77  data.node2 = node2;
78  data.result = 0;
79 
80  g_hash_table_foreach(node1->details->utilization, do_compare_capacity1, &data);
81  g_hash_table_foreach(node2->details->utilization, do_compare_capacity2, &data);
82 
83  return data.result;
84 }
85 
86 struct calculate_data {
87  GHashTable *current_utilization;
88  gboolean plus;
89 };
90 
91 static void
92 do_calculate_utilization(gpointer key, gpointer value, gpointer user_data)
93 {
94  const char *current = NULL;
95  char *result = NULL;
96  struct calculate_data *data = user_data;
97 
98  current = g_hash_table_lookup(data->current_utilization, key);
99  if (data->plus) {
100  result = crm_itoa(crm_parse_int(current, "0") + crm_parse_int(value, "0"));
101  g_hash_table_replace(data->current_utilization, strdup(key), result);
102 
103  } else if (current) {
104  result = crm_itoa(crm_parse_int(current, "0") - crm_parse_int(value, "0"));
105  g_hash_table_replace(data->current_utilization, strdup(key), result);
106  }
107 }
108 
109 /* Specify 'plus' to FALSE when allocating
110  * Otherwise to TRUE when deallocating
111  */
112 void
113 calculate_utilization(GHashTable * current_utilization,
114  GHashTable * utilization, gboolean plus)
115 {
116  struct calculate_data data;
117 
118  data.current_utilization = current_utilization;
119  data.plus = plus;
120 
121  g_hash_table_foreach(utilization, do_calculate_utilization, &data);
122 }
123 
124 
125 struct capacity_data {
126  pe_node_t *node;
127  const char *rsc_id;
128  gboolean is_enough;
129 };
130 
131 static void
132 check_capacity(gpointer key, gpointer value, gpointer user_data)
133 {
134  int required = 0;
135  int remaining = 0;
136  struct capacity_data *data = user_data;
137 
138  required = crm_parse_int(value, "0");
139  remaining = crm_parse_int(g_hash_table_lookup(data->node->details->utilization, key), "0");
140 
141  if (required > remaining) {
142  CRM_ASSERT(data->rsc_id);
143  CRM_ASSERT(data->node);
144 
145  crm_debug("Node %s does not have enough %s for %s: required=%d remaining=%d",
146  data->node->details->uname, (char *)key, data->rsc_id, required, remaining);
147  data->is_enough = FALSE;
148  }
149 }
150 
151 static gboolean
152 have_enough_capacity(pe_node_t * node, const char * rsc_id, GHashTable * utilization)
153 {
154  struct capacity_data data;
155 
156  data.node = node;
157  data.rsc_id = rsc_id;
158  data.is_enough = TRUE;
159 
160  g_hash_table_foreach(utilization, check_capacity, &data);
161 
162  return data.is_enough;
163 }
164 
165 
166 static void
167 native_add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc)
168 {
169  if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
170  return;
171  }
172 
173  calculate_utilization(all_utilization, rsc->utilization, TRUE);
174 }
175 
176 static void
177 add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc,
178  GListPtr all_rscs, pe_resource_t * orig_rsc)
179 {
180  if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
181  return;
182  }
183 
184  if (rsc->variant == pe_native) {
185  pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
186  orig_rsc->id, rsc->id);
187  native_add_unallocated_utilization(all_utilization, rsc);
188 
189  } else if (rsc->variant == pe_group) {
190  pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
191  orig_rsc->id, rsc->id);
192  group_add_unallocated_utilization(all_utilization, rsc, all_rscs);
193 
194  } else if (pe_rsc_is_clone(rsc)) {
195  GListPtr gIter1 = NULL;
196  gboolean existing = FALSE;
197 
198  /* Check if there's any child already existing in the list */
199  gIter1 = rsc->children;
200  for (; gIter1 != NULL; gIter1 = gIter1->next) {
201  pe_resource_t *child = (pe_resource_t *) gIter1->data;
202  GListPtr gIter2 = NULL;
203 
204  if (g_list_find(all_rscs, child)) {
205  existing = TRUE;
206 
207  } else {
208  /* Check if there's any child of another cloned group already existing in the list */
209  gIter2 = child->children;
210  for (; gIter2 != NULL; gIter2 = gIter2->next) {
211  pe_resource_t *grandchild = (pe_resource_t *) gIter2->data;
212 
213  if (g_list_find(all_rscs, grandchild)) {
214  pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
215  orig_rsc->id, child->id);
216  add_unallocated_utilization(all_utilization, child, all_rscs, orig_rsc);
217  existing = TRUE;
218  break;
219  }
220  }
221  }
222  }
223 
224  // rsc->children is always non-NULL but this makes static analysis happy
225  if (!existing && (rsc->children != NULL)) {
226  pe_resource_t *first_child = (pe_resource_t *) rsc->children->data;
227 
228  pe_rsc_trace(orig_rsc, "%s: Adding %s as colocated utilization",
229  orig_rsc->id, ID(first_child->xml));
230  add_unallocated_utilization(all_utilization, first_child, all_rscs, orig_rsc);
231  }
232  }
233 }
234 
235 static GHashTable *
236 sum_unallocated_utilization(pe_resource_t * rsc, GListPtr colocated_rscs)
237 {
238  GListPtr gIter = NULL;
239  GListPtr all_rscs = NULL;
240  GHashTable *all_utilization = crm_str_table_new();
241 
242  all_rscs = g_list_copy(colocated_rscs);
243  if (g_list_find(all_rscs, rsc) == FALSE) {
244  all_rscs = g_list_append(all_rscs, rsc);
245  }
246 
247  for (gIter = all_rscs; gIter != NULL; gIter = gIter->next) {
248  pe_resource_t *listed_rsc = (pe_resource_t *) gIter->data;
249 
250  if (!pcmk_is_set(listed_rsc->flags, pe_rsc_provisional)) {
251  continue;
252  }
253 
254  pe_rsc_trace(rsc, "%s: Processing unallocated colocated %s", rsc->id, listed_rsc->id);
255  add_unallocated_utilization(all_utilization, listed_rsc, all_rscs, rsc);
256  }
257 
258  g_list_free(all_rscs);
259 
260  return all_utilization;
261 }
262 
263 static GListPtr
264 find_colocated_rscs(GListPtr colocated_rscs, pe_resource_t * rsc, pe_resource_t * orig_rsc)
265 {
266  GListPtr gIter = NULL;
267 
268  if (rsc == NULL) {
269  return colocated_rscs;
270 
271  } else if (g_list_find(colocated_rscs, rsc)) {
272  return colocated_rscs;
273  }
274 
275  crm_trace("%s: %s is supposed to be colocated with %s", orig_rsc->id, rsc->id, orig_rsc->id);
276  colocated_rscs = g_list_append(colocated_rscs, rsc);
277 
278  for (gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) {
279  rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
280  pe_resource_t *rsc_rh = constraint->rsc_rh;
281 
282  /* Break colocation loop */
283  if (rsc_rh == orig_rsc) {
284  continue;
285  }
286 
287  if (constraint->score == INFINITY
288  && filter_colocation_constraint(rsc, rsc_rh, constraint, TRUE) == influence_rsc_location) {
289 
290  if (rsc_rh->variant == pe_group) {
291  /* Need to use group_variant_data */
292  colocated_rscs = group_find_colocated_rscs(colocated_rscs, rsc_rh, orig_rsc);
293 
294  } else {
295  colocated_rscs = find_colocated_rscs(colocated_rscs, rsc_rh, orig_rsc);
296  }
297  }
298  }
299 
300  for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
301  rsc_colocation_t *constraint = (rsc_colocation_t *) gIter->data;
302  pe_resource_t *rsc_lh = constraint->rsc_lh;
303 
304  /* Break colocation loop */
305  if (rsc_lh == orig_rsc) {
306  continue;
307  }
308 
309  if (pe_rsc_is_clone(rsc_lh) == FALSE && pe_rsc_is_clone(rsc)) {
310  /* We do not know if rsc_lh will be colocated with orig_rsc in this case */
311  continue;
312  }
313 
314  if (constraint->score == INFINITY
315  && filter_colocation_constraint(rsc_lh, rsc, constraint, TRUE) == influence_rsc_location) {
316 
317  if (rsc_lh->variant == pe_group) {
318  /* Need to use group_variant_data */
319  colocated_rscs = group_find_colocated_rscs(colocated_rscs, rsc_lh, orig_rsc);
320 
321  } else {
322  colocated_rscs = find_colocated_rscs(colocated_rscs, rsc_lh, orig_rsc);
323  }
324  }
325  }
326 
327  return colocated_rscs;
328 }
329 
330 void
332 {
333  CRM_CHECK(rsc && prefer && data_set, return);
334  if (!pcmk__str_eq(data_set->placement_strategy, "default", pcmk__str_casei)) {
335  GHashTableIter iter;
336  GListPtr colocated_rscs = NULL;
337  gboolean any_capable = FALSE;
338  pe_node_t *node = NULL;
339 
340  colocated_rscs = find_colocated_rscs(colocated_rscs, rsc, rsc);
341  if (colocated_rscs) {
342  GHashTable *unallocated_utilization = NULL;
343  char *rscs_id = crm_strdup_printf("%s and its colocated resources",
344  rsc->id);
345  pe_node_t *most_capable_node = NULL;
346 
347  unallocated_utilization = sum_unallocated_utilization(rsc, colocated_rscs);
348 
349  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
350  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
351  if (can_run_resources(node) == FALSE || node->weight < 0) {
352  continue;
353  }
354 
355  if (have_enough_capacity(node, rscs_id, unallocated_utilization)) {
356  any_capable = TRUE;
357  }
358 
359  if (most_capable_node == NULL ||
360  compare_capacity(node, most_capable_node) < 0) {
361  /* < 0 means 'node' is more capable */
362  most_capable_node = node;
363  }
364  }
365 
366  if (any_capable) {
367  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
368  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
369  if (can_run_resources(node) == FALSE || node->weight < 0) {
370  continue;
371  }
372 
373  if (have_enough_capacity(node, rscs_id, unallocated_utilization) == FALSE) {
374  pe_rsc_debug(rsc,
375  "Resource %s and its colocated resources"
376  " cannot be allocated to node %s: not enough capacity",
377  rsc->id, node->details->uname);
378  resource_location(rsc, node, -INFINITY, "__limit_utilization__", data_set);
379  }
380  }
381 
382  } else if (*prefer == NULL) {
383  *prefer = most_capable_node;
384  }
385 
386  if (unallocated_utilization) {
387  g_hash_table_destroy(unallocated_utilization);
388  }
389 
390  g_list_free(colocated_rscs);
391  free(rscs_id);
392  }
393 
394  if (any_capable == FALSE) {
395  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
396  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
397  if (can_run_resources(node) == FALSE || node->weight < 0) {
398  continue;
399  }
400 
401  if (have_enough_capacity(node, rsc->id, rsc->utilization) == FALSE) {
402  pe_rsc_debug(rsc,
403  "Resource %s cannot be allocated to node %s:"
404  " not enough capacity",
405  rsc->id, node->details->uname);
406  resource_location(rsc, node, -INFINITY, "__limit_utilization__", data_set);
407  }
408  }
409  }
410  pe__show_node_weights(true, rsc, "Post-utilization", rsc->allowed_nodes);
411  }
412 }
413 
414 #define VARIANT_GROUP 1
415 #include <lib/pengine/variant.h>
416 
417 GListPtr
418 group_find_colocated_rscs(GListPtr colocated_rscs, pe_resource_t * rsc, pe_resource_t * orig_rsc)
419 {
420  group_variant_data_t *group_data = NULL;
421 
422  get_group_variant_data(group_data, rsc);
423  if (group_data->colocated || pe_rsc_is_clone(rsc->parent)) {
424  GListPtr gIter = rsc->children;
425 
426  for (; gIter != NULL; gIter = gIter->next) {
427  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
428 
429  colocated_rscs = find_colocated_rscs(colocated_rscs, child_rsc, orig_rsc);
430  }
431 
432  } else {
433  if (group_data->first_child) {
434  colocated_rscs = find_colocated_rscs(colocated_rscs, group_data->first_child, orig_rsc);
435  }
436  }
437 
438  colocated_rscs = find_colocated_rscs(colocated_rscs, rsc, orig_rsc);
439 
440  return colocated_rscs;
441 }
442 
443 static void
444 group_add_unallocated_utilization(GHashTable * all_utilization, pe_resource_t * rsc,
445  GListPtr all_rscs)
446 {
447  group_variant_data_t *group_data = NULL;
448 
449  get_group_variant_data(group_data, rsc);
450  if (group_data->colocated || pe_rsc_is_clone(rsc->parent)) {
451  GListPtr gIter = rsc->children;
452 
453  for (; gIter != NULL; gIter = gIter->next) {
454  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
455 
456  if (pcmk_is_set(child_rsc->flags, pe_rsc_provisional) &&
457  g_list_find(all_rscs, child_rsc) == FALSE) {
458  native_add_unallocated_utilization(all_utilization, child_rsc);
459  }
460  }
461 
462  } else {
463  if (group_data->first_child &&
464  pcmk_is_set(group_data->first_child->flags, pe_rsc_provisional) &&
465  g_list_find(all_rscs, group_data->first_child) == FALSE) {
466  native_add_unallocated_utilization(all_utilization, group_data->first_child);
467  }
468  }
469 }
470 
471 
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:215
void process_utilization(pe_resource_t *rsc, pe_node_t **prefer, pe_working_set_t *data_set)
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:19
#define INFINITY
Definition: crm.h:95
void calculate_utilization(GHashTable *current_utilization, GHashTable *utilization, gboolean plus)
xmlNode * xml
Definition: pe_types.h:310
#define pe__show_node_weights(level, rsc, text, nodes)
Definition: internal.h:393
int crm_parse_int(const char *text, const char *default_text)
Parse an integer value from a string.
Definition: strings.c:134
void resource_location(pe_resource_t *rsc, pe_node_t *node, int score, const char *tag, pe_working_set_t *data_set)
Definition: utils.c:1669
GListPtr rsc_cons
Definition: pe_types.h:344
gboolean can_run_resources(const pe_node_t *node)
#define pe_rsc_provisional
Definition: pe_types.h:247
#define crm_debug(fmt, args...)
Definition: logging.h:352
enum filter_colocation_res filter_colocation_constraint(pe_resource_t *rsc_lh, pe_resource_t *rsc_rh, rsc_colocation_t *constraint, gboolean preview)
int compare_capacity(const pe_node_t *node1, const pe_node_t *node2)
#define crm_trace(fmt, args...)
Definition: logging.h:353
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:196
struct pe_node_shared_s * details
Definition: pe_types.h:233
unsigned long long flags
Definition: pe_types.h:335
GHashTable * utilization
Definition: pe_types.h:362
GListPtr rsc_cons_lhs
Definition: pe_types.h:343
pe_resource_t * rsc_lh
enum pe_obj_types variant
Definition: pe_types.h:317
const char * placement_strategy
Definition: pe_types.h:135
GListPtr children
Definition: pe_types.h:364
#define CRM_ASSERT(expr)
Definition: results.h:42
GHashTable * utilization
Definition: pe_types.h:224
char data[0]
Definition: internal.h:90
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:20
#define ID(x)
Definition: msg_xml.h:425
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
GList * GListPtr
Definition: crm.h:214
pe_resource_t * parent
Definition: pe_types.h:315
pe_resource_t * rsc_rh
char * id
Definition: pe_types.h:308
GHashTable * allowed_nodes
Definition: pe_types.h:355