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