pacemaker  2.1.2-ada5c3b36
Scalable High-Availability cluster resource manager
pcmk_sched_promotable.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2021 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 
12 #include <crm/msg_xml.h>
13 #include <pacemaker-internal.h>
14 
15 #include "libpacemaker_private.h"
16 
17 #define VARIANT_CLONE 1
18 #include <lib/pengine/variant.h>
19 
20 extern gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
21 
22 extern bool pcmk__is_daemon;
23 
24 static void
25 child_promoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type,
26  pe_resource_t * rsc, pe_resource_t * child, pe_resource_t * last,
27  pe_working_set_t * data_set)
28 {
29  if (child == NULL) {
30  if (clone_data->ordered && last != NULL) {
31  pe_rsc_trace(rsc, "Ordered version (last node)");
32  /* last child promote before promoted started */
34  type, data_set);
35  }
36  return;
37  }
38 
39  /* child promote before global promoted */
41  data_set);
42 
43  /* global promote before child promote */
45  data_set);
46 
47  if (clone_data->ordered) {
48  pe_rsc_trace(rsc, "Ordered version");
49  if (last == NULL) {
50  /* global promote before first child promote */
51  last = rsc;
52 
53  }
54  /* else: child/child relative promote */
55  pcmk__order_starts(last, child, type, data_set);
57  type, data_set);
58 
59  } else {
60  pe_rsc_trace(rsc, "Un-ordered version");
61  }
62 }
63 
64 static void
65 child_demoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type,
66  pe_resource_t * rsc, pe_resource_t * child, pe_resource_t * last,
67  pe_working_set_t * data_set)
68 {
69  if (child == NULL) {
70  if (clone_data->ordered && last != NULL) {
71  pe_rsc_trace(rsc, "Ordered version (last node)");
72  /* global demote before first child demote */
74  pe_order_optional, data_set);
75  }
76  return;
77  }
78 
79  /* child demote before global demoted */
82 
83  /* global demote before child demote */
86 
87  if (clone_data->ordered && last != NULL) {
88  pe_rsc_trace(rsc, "Ordered version");
89 
90  /* child/child relative demote */
92  data_set);
93 
94  } else if (clone_data->ordered) {
95  pe_rsc_trace(rsc, "Ordered version (1st node)");
96  /* first child stop before global stopped */
98  data_set);
99 
100  } else {
101  pe_rsc_trace(rsc, "Un-ordered version");
102  }
103 }
104 
105 static void
106 check_promotable_actions(pe_resource_t *rsc, gboolean *demoting,
107  gboolean *promoting)
108 {
109  GList *gIter = NULL;
110 
111  if (rsc->children) {
112  gIter = rsc->children;
113  for (; gIter != NULL; gIter = gIter->next) {
114  pe_resource_t *child = (pe_resource_t *) gIter->data;
115 
116  check_promotable_actions(child, demoting, promoting);
117  }
118  return;
119  }
120 
121  CRM_ASSERT(demoting != NULL);
122  CRM_ASSERT(promoting != NULL);
123 
124  gIter = rsc->actions;
125  for (; gIter != NULL; gIter = gIter->next) {
126  pe_action_t *action = (pe_action_t *) gIter->data;
127 
128  if (*promoting && *demoting) {
129  return;
130 
131  } else if (pcmk_is_set(action->flags, pe_action_optional)) {
132  continue;
133 
134  } else if (pcmk__str_eq(RSC_DEMOTE, action->task, pcmk__str_casei)) {
135  *demoting = TRUE;
136 
137  } else if (pcmk__str_eq(RSC_PROMOTE, action->task, pcmk__str_casei)) {
138  *promoting = TRUE;
139  }
140  }
141 }
142 
143 static void
144 apply_promoted_location(pe_resource_t *child, GList *location_constraints,
145  pe_node_t *chosen)
146 {
147  CRM_CHECK(child && chosen, return);
148  for (GList *gIter = location_constraints; gIter; gIter = gIter->next) {
149  pe_node_t *cons_node = NULL;
150  pe__location_t *cons = gIter->data;
151 
152  if (cons->role_filter == RSC_ROLE_PROMOTED) {
153  pe_rsc_trace(child, "Applying %s to %s", cons->id, child->id);
154  cons_node = pe_find_node_id(cons->node_list_rh, chosen->details->id);
155  }
156  if (cons_node != NULL) {
157  int new_priority = pe__add_scores(child->priority,
158  cons_node->weight);
159 
160  pe_rsc_trace(child, "\t%s[%s]: %d -> %d (%d)",
161  child->id, cons_node->details->uname, child->priority,
162  new_priority, cons_node->weight);
163  child->priority = new_priority;
164  }
165  }
166 }
167 
168 static pe_node_t *
169 guest_location(pe_node_t *guest_node)
170 {
171  pe_resource_t *guest = guest_node->details->remote_rsc->container;
172 
173  return guest->fns->location(guest, NULL, FALSE);
174 }
175 
176 static pe_node_t *
177 node_to_be_promoted_on(pe_resource_t *rsc)
178 {
179  pe_node_t *node = NULL;
180  pe_node_t *local_node = NULL;
181  pe_resource_t *parent = uber_parent(rsc);
182  clone_variant_data_t *clone_data = NULL;
183 
184 #if 0
185  enum rsc_role_e role = RSC_ROLE_UNKNOWN;
186 
187  role = rsc->fns->state(rsc, FALSE);
188  crm_info("%s role: %s", rsc->id, role2text(role));
189 #endif
190 
191  if (rsc->children) {
192  GList *gIter = rsc->children;
193 
194  for (; gIter != NULL; gIter = gIter->next) {
195  pe_resource_t *child = (pe_resource_t *) gIter->data;
196 
197  if (node_to_be_promoted_on(child) == NULL) {
198  pe_rsc_trace(rsc, "Child %s of %s can't be promoted", child->id, rsc->id);
199  return NULL;
200  }
201  }
202  }
203 
204  node = rsc->fns->location(rsc, NULL, FALSE);
205  if (node == NULL) {
206  pe_rsc_trace(rsc, "%s cannot be promoted: not allocated", rsc->id);
207  return NULL;
208 
209  } else if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
210  if (rsc->fns->state(rsc, TRUE) == RSC_ROLE_PROMOTED) {
211  crm_notice("Forcing unmanaged instance %s to remain promoted on %s",
212  rsc->id, node->details->uname);
213 
214  } else {
215  return NULL;
216  }
217 
218  } else if (rsc->priority < 0) {
219  pe_rsc_trace(rsc, "%s cannot be promoted: preference: %d",
220  rsc->id, rsc->priority);
221  return NULL;
222 
223  } else if (can_run_resources(node) == FALSE) {
224  crm_trace("Node can't run any resources: %s", node->details->uname);
225  return NULL;
226 
227  /* @TODO It's possible this check should be done in can_run_resources()
228  * instead. We should investigate all its callers to figure out whether that
229  * would be a good idea.
230  */
231  } else if (pe__is_guest_node(node) && (guest_location(node) == NULL)) {
232  pe_rsc_trace(rsc, "%s cannot be promoted: guest %s not allocated",
233  rsc->id, node->details->remote_rsc->container->id);
234  return NULL;
235  }
236 
237  get_clone_variant_data(clone_data, parent);
238  local_node = pe_hash_table_lookup(parent->allowed_nodes, node->details->id);
239 
240  if (local_node == NULL) {
241  crm_err("%s cannot run on %s: node not allowed", rsc->id, node->details->uname);
242  return NULL;
243 
244  } else if ((local_node->count < clone_data->promoted_node_max)
245  || !pcmk_is_set(rsc->flags, pe_rsc_managed)) {
246  return local_node;
247 
248  } else {
249  pe_rsc_trace(rsc, "%s cannot be promoted on %s: node full",
250  rsc->id, node->details->uname);
251  }
252 
253  return NULL;
254 }
255 
256 static gint
257 sort_promotable_instance(gconstpointer a, gconstpointer b, gpointer data_set)
258 {
259  int rc;
260  enum rsc_role_e role1 = RSC_ROLE_UNKNOWN;
261  enum rsc_role_e role2 = RSC_ROLE_UNKNOWN;
262 
263  const pe_resource_t *resource1 = (const pe_resource_t *)a;
264  const pe_resource_t *resource2 = (const pe_resource_t *)b;
265 
266  CRM_ASSERT(resource1 != NULL);
267  CRM_ASSERT(resource2 != NULL);
268 
269  role1 = resource1->fns->state(resource1, TRUE);
270  role2 = resource2->fns->state(resource2, TRUE);
271 
272  rc = sort_rsc_index(a, b);
273  if (rc != 0) {
274  crm_trace("%s %c %s (index)", resource1->id, rc < 0 ? '<' : '>', resource2->id);
275  return rc;
276  }
277 
278  if (role1 > role2) {
279  crm_trace("%s %c %s (role)", resource1->id, '<', resource2->id);
280  return -1;
281 
282  } else if (role1 < role2) {
283  crm_trace("%s %c %s (role)", resource1->id, '>', resource2->id);
284  return 1;
285  }
286 
287  return sort_clone_instance(a, b, data_set);
288 }
289 
290 static void
291 promotion_order(pe_resource_t *rsc, pe_working_set_t *data_set)
292 {
293  GList *gIter = NULL;
294  pe_node_t *node = NULL;
295  pe_node_t *chosen = NULL;
296  clone_variant_data_t *clone_data = NULL;
297  char score[33];
298  size_t len = sizeof(score);
299 
300  get_clone_variant_data(clone_data, rsc);
301 
302  if (clone_data->added_promoted_constraints) {
303  return;
304  }
305  clone_data->added_promoted_constraints = true;
306  pe_rsc_trace(rsc, "Merging weights for %s", rsc->id);
308 
309  for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
310  pe_resource_t *child = (pe_resource_t *) gIter->data;
311 
312  pe_rsc_trace(rsc, "Sort index: %s = %d", child->id, child->sort_index);
313  }
314  pe__show_node_weights(true, rsc, "Before", rsc->allowed_nodes, data_set);
315 
316  gIter = rsc->children;
317  for (; gIter != NULL; gIter = gIter->next) {
318  pe_resource_t *child = (pe_resource_t *) gIter->data;
319 
320  chosen = child->fns->location(child, NULL, FALSE);
321  if (chosen == NULL || child->sort_index < 0) {
322  pe_rsc_trace(rsc, "Skipping %s", child->id);
323  continue;
324  }
325 
326  node = (pe_node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id);
327  CRM_ASSERT(node != NULL);
328  // Add promotion preferences and rsc_location scores when role=Promoted
329  score2char_stack(child->sort_index, score, len);
330  pe_rsc_trace(rsc, "Adding %s to %s from %s", score,
331  node->details->uname, child->id);
332  node->weight = pe__add_scores(child->sort_index, node->weight);
333  }
334 
335  pe__show_node_weights(true, rsc, "Middle", rsc->allowed_nodes, data_set);
336 
337  gIter = rsc->rsc_cons;
338  for (; gIter != NULL; gIter = gIter->next) {
339  pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
340 
341  /* (Re-)add location preferences of resources that a promoted instance
342  * should/must be colocated with.
343  */
344  if (constraint->dependent_role == RSC_ROLE_PROMOTED) {
345  enum pe_weights flags = constraint->score == INFINITY ? 0 : pe_weights_rollback;
346 
347  pe_rsc_trace(rsc, "RHS: %s with %s: %d",
348  constraint->dependent->id, constraint->primary->id,
349  constraint->score);
350  rsc->allowed_nodes = constraint->primary->cmds->merge_weights(
351  constraint->primary, rsc->id, rsc->allowed_nodes,
352  constraint->node_attribute,
353  constraint->score / (float) INFINITY, flags);
354  }
355  }
356 
357  gIter = rsc->rsc_cons_lhs;
358  for (; gIter != NULL; gIter = gIter->next) {
359  pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
360 
361  if (!pcmk__colocation_has_influence(constraint, NULL)) {
362  continue;
363  }
364 
365  /* (Re-)add location preferences of resources that wish to be colocated
366  * with a promoted instance.
367  */
368  if (constraint->primary_role == RSC_ROLE_PROMOTED) {
369  pe_rsc_trace(rsc, "LHS: %s with %s: %d",
370  constraint->dependent->id, constraint->primary->id,
371  constraint->score);
372  rsc->allowed_nodes = constraint->dependent->cmds->merge_weights(
373  constraint->dependent, rsc->id, rsc->allowed_nodes,
374  constraint->node_attribute,
375  constraint->score / (float) INFINITY,
377  }
378  }
379 
380  gIter = rsc->rsc_tickets;
381  for (; gIter != NULL; gIter = gIter->next) {
382  rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) gIter->data;
383 
384  if ((rsc_ticket->role_lh == RSC_ROLE_PROMOTED)
385  && (rsc_ticket->ticket->granted == FALSE || rsc_ticket->ticket->standby)) {
386  resource_location(rsc, NULL, -INFINITY, "__stateful_without_ticket__", data_set);
387  }
388  }
389 
390  pe__show_node_weights(true, rsc, "After", rsc->allowed_nodes, data_set);
391 
392  /* write them back and sort */
393 
394  gIter = rsc->children;
395  for (; gIter != NULL; gIter = gIter->next) {
396  pe_resource_t *child = (pe_resource_t *) gIter->data;
397 
398  chosen = child->fns->location(child, NULL, FALSE);
399  if (!pcmk_is_set(child->flags, pe_rsc_managed)
400  && (child->next_role == RSC_ROLE_PROMOTED)) {
401  child->sort_index = INFINITY;
402 
403  } else if (chosen == NULL || child->sort_index < 0) {
404  pe_rsc_trace(rsc, "%s: %d", child->id, child->sort_index);
405 
406  } else {
407  node = (pe_node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id);
408  CRM_ASSERT(node != NULL);
409 
410  child->sort_index = node->weight;
411  }
412  pe_rsc_trace(rsc, "Set sort index: %s = %d", child->id, child->sort_index);
413  }
414 
415  rsc->children = g_list_sort_with_data(rsc->children,
416  sort_promotable_instance, data_set);
418 }
419 
420 static gboolean
421 filter_anonymous_instance(pe_resource_t *rsc, const pe_node_t *node)
422 {
423  GList *rIter = NULL;
424  char *key = clone_strip(rsc->id);
425  pe_resource_t *parent = uber_parent(rsc);
426 
427  for (rIter = parent->children; rIter; rIter = rIter->next) {
428  /* If there is an active instance on the node, only it receives the
429  * promotion score. Use ->find_rsc() in case this is a cloned group.
430  */
431  pe_resource_t *child = rIter->data;
432  pe_resource_t *active = parent->fns->find_rsc(child, key, node, pe_find_clone|pe_find_current);
433 
434  if(rsc == active) {
435  pe_rsc_trace(rsc, "Found %s for %s active on %s: done", active->id, key, node->details->uname);
436  free(key);
437  return TRUE;
438  } else if(active) {
439  pe_rsc_trace(rsc, "Found %s for %s on %s: not %s", active->id, key, node->details->uname, rsc->id);
440  free(key);
441  return FALSE;
442  } else {
443  pe_rsc_trace(rsc, "%s on %s: not active", key, node->details->uname);
444  }
445  }
446 
447  for (rIter = parent->children; rIter; rIter = rIter->next) {
448  pe_resource_t *child = rIter->data;
449 
450  /*
451  * We know it's not running, but any score will still count if
452  * the instance has been probed on $node
453  *
454  * Again use ->find_rsc() because we might be a cloned group
455  * and knowing that other members of the group are known here
456  * implies nothing
457  */
458  rsc = parent->fns->find_rsc(child, key, NULL, pe_find_clone);
459  CRM_LOG_ASSERT(rsc);
460  if(rsc) {
461  pe_rsc_trace(rsc, "Checking %s for %s on %s", rsc->id, key, node->details->uname);
462  if (g_hash_table_lookup(rsc->known_on, node->details->id)) {
463  free(key);
464  return TRUE;
465  }
466  }
467  }
468  free(key);
469  return FALSE;
470 }
471 
472 static const char *
473 lookup_promotion_score(pe_resource_t *rsc, const pe_node_t *node, const char *name)
474 {
475  const char *attr_value = NULL;
476 
477  if (node && name) {
478  char *attr_name = pcmk_promotion_score_name(name);
479 
480  attr_value = pe_node_attribute_calculated(node, attr_name, rsc);
481  free(attr_name);
482  }
483  return attr_value;
484 }
485 
486 static int
487 promotion_score(pe_resource_t *rsc, const pe_node_t *node, int not_set_value)
488 {
489  char *name = rsc->id;
490  const char *attr_value = NULL;
491  int score = not_set_value;
492  pe_node_t *match = NULL;
493 
494  CRM_CHECK(node != NULL, return not_set_value);
495 
496  if (rsc->children) {
497  GList *gIter = rsc->children;
498 
499  for (; gIter != NULL; gIter = gIter->next) {
500  pe_resource_t *child = (pe_resource_t *) gIter->data;
501  int c_score = promotion_score(child, node, not_set_value);
502 
503  if (score == not_set_value) {
504  score = c_score;
505  } else {
506  score += c_score;
507  }
508  }
509  return score;
510  }
511 
512  if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
513  && filter_anonymous_instance(rsc, node)) {
514 
515  pe_rsc_trace(rsc, "Anonymous clone %s is allowed on %s", rsc->id, node->details->uname);
516 
517  } else if (rsc->running_on || g_hash_table_size(rsc->known_on)) {
518  /* If we've probed and/or started the resource anywhere, consider
519  * promotion scores only from nodes where we know the status. However,
520  * if the status of all nodes is unknown (e.g. cluster startup),
521  * skip this code, to make sure we take into account any permanent
522  * promotion scores set previously.
523  */
524  pe_node_t *known = pe_hash_table_lookup(rsc->known_on, node->details->id);
525 
526  match = pe_find_node_id(rsc->running_on, node->details->id);
527  if ((match == NULL) && (known == NULL)) {
528  pe_rsc_trace(rsc, "skipping %s (aka. %s) promotion score on %s because inactive",
529  rsc->id, rsc->clone_name, node->details->uname);
530  return score;
531  }
532  }
533 
534  match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
535  if (match == NULL) {
536  return score;
537 
538  } else if (match->weight < 0) {
539  pe_rsc_trace(rsc, "%s on %s has score: %d - ignoring",
540  rsc->id, match->details->uname, match->weight);
541  return score;
542  }
543 
544  if (rsc->clone_name) {
545  /* Use the name the lrm knows this resource as,
546  * since that's what crm_attribute --promotion would have used
547  */
548  name = rsc->clone_name;
549  }
550 
551  attr_value = lookup_promotion_score(rsc, node, name);
552  pe_rsc_trace(rsc, "promotion score for %s on %s = %s",
553  name, node->details->uname, crm_str(attr_value));
554 
555  if ((attr_value == NULL) && !pcmk_is_set(rsc->flags, pe_rsc_unique)) {
556  /* If we don't have any LRM history yet, we won't have clone_name -- in
557  * that case, for anonymous clones, try the resource name without any
558  * instance number.
559  */
560  name = clone_strip(rsc->id);
561  if (strcmp(rsc->id, name)) {
562  attr_value = lookup_promotion_score(rsc, node, name);
563  pe_rsc_trace(rsc, "stripped promotion score for %s on %s = %s",
564  name, node->details->uname, crm_str(attr_value));
565  }
566  free(name);
567  }
568 
569  if (attr_value != NULL) {
570  score = char2score(attr_value);
571  }
572 
573  return score;
574 }
575 
576 void
578 {
579  int score, new_score;
580  GList *gIter = rsc->children;
581  clone_variant_data_t *clone_data = NULL;
582 
583  get_clone_variant_data(clone_data, rsc);
584 
585  if (clone_data->added_promotion_scores) {
586  /* Make sure we only do this once */
587  return;
588  }
589 
590  clone_data->added_promotion_scores = true;
591 
592  for (; gIter != NULL; gIter = gIter->next) {
593  GHashTableIter iter;
594  pe_node_t *node = NULL;
595  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
596 
597  g_hash_table_iter_init(&iter, child_rsc->allowed_nodes);
598  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
599  if (can_run_resources(node) == FALSE) {
600  /* This node will never be promoted, so don't apply the
601  * promotion score, as that may lead to clone shuffling.
602  */
603  continue;
604  }
605 
606  score = promotion_score(child_rsc, node, 0);
607  if (score > 0) {
608  new_score = pe__add_scores(node->weight, score);
609  if (new_score != node->weight) {
610  pe_rsc_trace(rsc, "\t%s: Updating preference for %s (%d->%d)",
611  child_rsc->id, node->details->uname, node->weight, new_score);
612  node->weight = new_score;
613  }
614  }
615 
616  new_score = QB_MAX(child_rsc->priority, score);
617  if (new_score != child_rsc->priority) {
618  pe_rsc_trace(rsc, "\t%s: Updating priority (%d->%d)",
619  child_rsc->id, child_rsc->priority, new_score);
620  child_rsc->priority = new_score;
621  }
622  }
623  }
624 }
625 
626 static void
627 set_role_unpromoted(pe_resource_t *rsc, bool current)
628 {
629  GList *gIter = rsc->children;
630 
631  if (current) {
632  if (rsc->role == RSC_ROLE_STARTED) {
633  rsc->role = RSC_ROLE_UNPROMOTED;
634  }
635 
636  } else {
637  GList *allocated = NULL;
638 
639  rsc->fns->location(rsc, &allocated, FALSE);
641  "unpromoted instance");
642  g_list_free(allocated);
643  }
644 
645  for (; gIter != NULL; gIter = gIter->next) {
646  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
647 
648  set_role_unpromoted(child_rsc, current);
649  }
650 }
651 
652 static void
653 set_role_promoted(pe_resource_t *rsc, gpointer user_data)
654 {
655  if (rsc->next_role == RSC_ROLE_UNKNOWN) {
656  pe__set_next_role(rsc, RSC_ROLE_PROMOTED, "promoted instance");
657  }
658 
659  g_list_foreach(rsc->children, (GFunc) set_role_promoted, NULL);
660 }
661 
662 pe_node_t *
664 {
665  int promoted = 0;
666  GList *gIter = NULL;
667  GList *gIter2 = NULL;
668  GHashTableIter iter;
669  pe_node_t *node = NULL;
670  pe_node_t *chosen = NULL;
671  enum rsc_role_e next_role = RSC_ROLE_UNKNOWN;
672  char score[33];
673  size_t len = sizeof(score);
674  clone_variant_data_t *clone_data = NULL;
675 
676  get_clone_variant_data(clone_data, rsc);
677 
678  // Repurpose count to track the number of promoted instances allocated
679  g_hash_table_iter_init(&iter, rsc->allowed_nodes);
680  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
681  node->count = 0;
682  }
683 
684  /*
685  * assign priority
686  */
687  for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
688  GList *list = NULL;
689  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
690 
691  pe_rsc_trace(rsc, "Assigning priority for %s: %s", child_rsc->id,
692  role2text(child_rsc->next_role));
693 
694  if (child_rsc->fns->state(child_rsc, TRUE) == RSC_ROLE_STARTED) {
695  set_role_unpromoted(child_rsc, true);
696  }
697 
698  chosen = child_rsc->fns->location(child_rsc, &list, FALSE);
699  if (pcmk__list_of_multiple(list)) {
700  pcmk__config_err("Cannot promote non-colocated child %s",
701  child_rsc->id);
702  }
703 
704  g_list_free(list);
705  if (chosen == NULL) {
706  continue;
707  }
708 
709  next_role = child_rsc->fns->state(child_rsc, FALSE);
710  switch (next_role) {
711  case RSC_ROLE_STARTED:
712  case RSC_ROLE_UNKNOWN:
713  /*
714  * Default to -1 if no value is set
715  *
716  * This allows instances eligible for promotion to be specified
717  * based solely on rsc_location constraints,
718  * but prevents anyone from being promoted if
719  * neither a constraint nor a promotion score is present
720  */
721  child_rsc->priority = promotion_score(child_rsc, chosen, -1);
722  break;
723 
724  case RSC_ROLE_UNPROMOTED:
725  case RSC_ROLE_STOPPED:
726  child_rsc->priority = -INFINITY;
727  break;
728  case RSC_ROLE_PROMOTED:
729  /* We will arrive here if we're re-creating actions after a stonith
730  */
731  break;
732  default:
733  CRM_CHECK(FALSE /* unhandled */ ,
734  crm_err("Unknown resource role: %d for %s", next_role, child_rsc->id));
735  }
736 
737  apply_promoted_location(child_rsc, child_rsc->rsc_location, chosen);
738  apply_promoted_location(child_rsc, rsc->rsc_location, chosen);
739 
740  for (gIter2 = child_rsc->rsc_cons; gIter2 != NULL; gIter2 = gIter2->next) {
741  pcmk__colocation_t *cons = (pcmk__colocation_t *) gIter2->data;
742 
743  child_rsc->cmds->rsc_colocation_lh(child_rsc, cons->primary, cons,
744  data_set);
745  }
746 
747  child_rsc->sort_index = child_rsc->priority;
748  pe_rsc_trace(rsc, "Assigning priority for %s: %d", child_rsc->id, child_rsc->priority);
749 
750  if (next_role == RSC_ROLE_PROMOTED) {
751  child_rsc->sort_index = INFINITY;
752  }
753  }
754 
755  pe__show_node_weights(true, rsc, "Pre merge", rsc->allowed_nodes, data_set);
756  promotion_order(rsc, data_set);
757 
758  // Choose the first N eligible instances to be promoted
759  for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
760  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
761  score2char_stack(child_rsc->sort_index, score, len);
762 
763  chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
765  if (data_set->priv != NULL) {
766  pcmk__output_t *out = data_set->priv;
767  out->message(out, "promotion-score", child_rsc, chosen, score);
768  }
769 
770  } else {
771  pe_rsc_trace(rsc, "%s promotion score on %s: %s", child_rsc->id,
772  (chosen? chosen->details->uname : "none"), score);
773  }
774 
775  chosen = NULL; /* nuke 'chosen' so that we don't promote more than the
776  * required number of instances
777  */
778 
779  if (child_rsc->sort_index < 0) {
780  pe_rsc_trace(rsc, "Not supposed to promote child: %s", child_rsc->id);
781 
782  } else if ((promoted < clone_data->promoted_max)
783  || !pcmk_is_set(rsc->flags, pe_rsc_managed)) {
784  chosen = node_to_be_promoted_on(child_rsc);
785  }
786 
787  pe_rsc_debug(rsc, "%s promotion score: %d", child_rsc->id, child_rsc->priority);
788 
789  if (chosen == NULL) {
790  set_role_unpromoted(child_rsc, false);
791  continue;
792 
793  } else if ((child_rsc->role < RSC_ROLE_PROMOTED)
794  && !pcmk_is_set(data_set->flags, pe_flag_have_quorum)
795  && data_set->no_quorum_policy == no_quorum_freeze) {
796  crm_notice("Resource %s cannot be elevated from %s to %s: no-quorum-policy=freeze",
797  child_rsc->id, role2text(child_rsc->role), role2text(child_rsc->next_role));
798  set_role_unpromoted(child_rsc, false);
799  continue;
800  }
801 
802  chosen->count++;
803  pe_rsc_info(rsc, "Promoting %s (%s %s)",
804  child_rsc->id, role2text(child_rsc->role), chosen->details->uname);
805  set_role_promoted(child_rsc, NULL);
806  promoted++;
807  }
808 
809  pe_rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
810  rsc->id, promoted, clone_data->promoted_max);
811 
812  return NULL;
813 }
814 
815 void
817 {
818  pe_action_t *action = NULL;
819  GList *gIter = rsc->children;
820  pe_action_t *action_complete = NULL;
821  gboolean any_promoting = FALSE;
822  gboolean any_demoting = FALSE;
823  pe_resource_t *last_promote_rsc = NULL;
824  pe_resource_t *last_demote_rsc = NULL;
825 
826  clone_variant_data_t *clone_data = NULL;
827 
828  get_clone_variant_data(clone_data, rsc);
829 
830  pe_rsc_debug(rsc, "Creating actions for %s", rsc->id);
831 
832  for (; gIter != NULL; gIter = gIter->next) {
833  gboolean child_promoting = FALSE;
834  gboolean child_demoting = FALSE;
835  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
836 
837  pe_rsc_trace(rsc, "Creating actions for %s", child_rsc->id);
838  child_rsc->cmds->create_actions(child_rsc, data_set);
839  check_promotable_actions(child_rsc, &child_demoting, &child_promoting);
840 
841  any_demoting = any_demoting || child_demoting;
842  any_promoting = any_promoting || child_promoting;
843  pe_rsc_trace(rsc, "Created actions for %s: %d %d", child_rsc->id, child_promoting,
844  child_demoting);
845  }
846 
847  /* promote */
848  action = create_pseudo_resource_op(rsc, RSC_PROMOTE, !any_promoting, TRUE, data_set);
849  action_complete = create_pseudo_resource_op(rsc, RSC_PROMOTED, !any_promoting, TRUE, data_set);
850  action_complete->priority = INFINITY;
851 
852  child_promoting_constraints(clone_data, pe_order_optional,
853  rsc, NULL, last_promote_rsc, data_set);
854 
855  if (clone_data->promote_notify == NULL) {
856  clone_data->promote_notify =
857  create_notification_boundaries(rsc, RSC_PROMOTE, action, action_complete, data_set);
858  }
859 
860  /* demote */
861  action = create_pseudo_resource_op(rsc, RSC_DEMOTE, !any_demoting, TRUE, data_set);
862  action_complete = create_pseudo_resource_op(rsc, RSC_DEMOTED, !any_demoting, TRUE, data_set);
863  action_complete->priority = INFINITY;
864 
865  child_demoting_constraints(clone_data, pe_order_optional, rsc, NULL, last_demote_rsc, data_set);
866 
867  if (clone_data->demote_notify == NULL) {
868  clone_data->demote_notify =
869  create_notification_boundaries(rsc, RSC_DEMOTE, action, action_complete, data_set);
870 
871  if (clone_data->promote_notify) {
872  /* If we ever wanted groups to have notifications we'd need to move this to native_internal_constraints() one day
873  * Requires exposing *_notify
874  */
875  order_actions(clone_data->stop_notify->post_done, clone_data->promote_notify->pre,
877  order_actions(clone_data->start_notify->post_done, clone_data->promote_notify->pre,
879  order_actions(clone_data->demote_notify->post_done, clone_data->promote_notify->pre,
881  order_actions(clone_data->demote_notify->post_done, clone_data->start_notify->pre,
883  order_actions(clone_data->demote_notify->post_done, clone_data->stop_notify->pre,
885  }
886  }
887 
888  /* restore the correct priority */
889 
890  gIter = rsc->children;
891  for (; gIter != NULL; gIter = gIter->next) {
892  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
893 
894  child_rsc->priority = rsc->priority;
895  }
896 }
897 
898 void
900 {
901  /* global stopped before start */
903  pe_order_optional, data_set);
904 
905  /* global stopped before promote */
907  pe_order_optional, data_set);
908 
909  /* global demoted before start */
911  pe_order_optional, data_set);
912 
913  /* global started before promote */
915  pe_order_optional, data_set);
916 
917  /* global demoted before stop */
919  pe_order_optional, data_set);
920 
921  /* global demote before demoted */
923  pe_order_optional, data_set);
924 
925  /* global demoted before promote */
927  pe_order_optional, data_set);
928 }
929 
930 
931 void
933 {
934  GList *gIter = rsc->children;
935  pe_resource_t *last_rsc = NULL;
936  clone_variant_data_t *clone_data = NULL;
937 
938  get_clone_variant_data(clone_data, rsc);
939 
940  promote_demote_constraints(rsc, data_set);
941 
942  for (; gIter != NULL; gIter = gIter->next) {
943  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
944 
945  /* child demote before promote */
946  pcmk__order_resource_actions(child_rsc, RSC_DEMOTE, child_rsc,
947  RSC_PROMOTE, pe_order_optional, data_set);
948 
949  child_promoting_constraints(clone_data, pe_order_optional,
950  rsc, child_rsc, last_rsc, data_set);
951 
952  child_demoting_constraints(clone_data, pe_order_optional,
953  rsc, child_rsc, last_rsc, data_set);
954 
955  last_rsc = child_rsc;
956  }
957 }
958 
959 static void
960 node_hash_update_one(GHashTable * hash, pe_node_t * other, const char *attr, int score)
961 {
962  GHashTableIter iter;
963  pe_node_t *node = NULL;
964  const char *value = NULL;
965 
966  if (other == NULL) {
967  return;
968 
969  } else if (attr == NULL) {
970  attr = CRM_ATTR_UNAME;
971  }
972 
973  value = pe_node_attribute_raw(other, attr);
974  g_hash_table_iter_init(&iter, hash);
975  while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
976  const char *tmp = pe_node_attribute_raw(node, attr);
977 
978  if (pcmk__str_eq(value, tmp, pcmk__str_casei)) {
979  crm_trace("%s: %d + %d", node->details->uname, node->weight, other->weight);
980  node->weight = pe__add_scores(node->weight, score);
981  }
982  }
983 }
984 
985 void
987  pcmk__colocation_t *constraint,
988  pe_working_set_t *data_set)
989 {
990  GList *gIter = NULL;
991 
992  if (pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
993  GList *affected_nodes = NULL;
994 
995  for (gIter = primary->children; gIter != NULL; gIter = gIter->next) {
996  pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
997  pe_node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
998  enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, FALSE);
999 
1000  pe_rsc_trace(primary, "Processing: %s", child_rsc->id);
1001  if ((chosen != NULL) && (next_role == constraint->primary_role)) {
1002  pe_rsc_trace(primary, "Applying: %s %s %s %d", child_rsc->id,
1003  role2text(next_role), chosen->details->uname, constraint->score);
1004  if (constraint->score < INFINITY) {
1005  node_hash_update_one(dependent->allowed_nodes, chosen,
1006  constraint->node_attribute, constraint->score);
1007  }
1008  affected_nodes = g_list_prepend(affected_nodes, chosen);
1009  }
1010  }
1011 
1012  /* Only do this if it's not a promoted-with-promoted colocation. Doing
1013  * this unconditionally would prevent unpromoted instances from being
1014  * started.
1015  */
1016  if ((constraint->dependent_role != RSC_ROLE_PROMOTED)
1017  || (constraint->primary_role != RSC_ROLE_PROMOTED)) {
1018 
1019  if (constraint->score >= INFINITY) {
1020  node_list_exclude(dependent->allowed_nodes, affected_nodes,
1021  TRUE);
1022  }
1023  }
1024  g_list_free(affected_nodes);
1025 
1026  } else if (constraint->dependent_role == RSC_ROLE_PROMOTED) {
1027  pe_resource_t *primary_instance;
1028 
1029  primary_instance = find_compatible_child(dependent, primary,
1030  constraint->primary_role,
1031  FALSE, data_set);
1032  if ((primary_instance == NULL) && (constraint->score >= INFINITY)) {
1033  pe_rsc_trace(dependent, "%s can't be promoted %s",
1034  dependent->id, constraint->id);
1035  dependent->priority = -INFINITY;
1036 
1037  } else if (primary_instance != NULL) {
1038  int new_priority = pe__add_scores(dependent->priority,
1039  constraint->score);
1040 
1041  pe_rsc_debug(dependent, "Applying %s to %s",
1042  constraint->id, dependent->id);
1043  pe_rsc_debug(dependent, "\t%s: %d->%d",
1044  dependent->id, dependent->priority, new_priority);
1045  dependent->priority = new_priority;
1046  }
1047  }
1048 
1049  return;
1050 }
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:225
enum rsc_role_e role_filter
Definition: internal.h:171
enum pe_quorum_policy no_quorum_policy
Definition: pe_types.h:149
#define RSC_STOP
Definition: crm.h:204
#define crm_notice(fmt, args...)
Definition: logging.h:359
GHashTable * known_on
Definition: pe_types.h:367
const char * pe_node_attribute_calculated(const pe_node_t *node, const char *name, const pe_resource_t *rsc)
Definition: common.c:596
#define pe_rsc_debug(rsc, fmt, args...)
Definition: internal.h:19
GList * rsc_tickets
Definition: pe_types.h:360
#define INFINITY
Definition: crm.h:99
GList * rsc_cons
Definition: pe_types.h:357
gint sort_rsc_index(gconstpointer a, gconstpointer b)
Definition: utils.c:335
#define pe__show_node_weights(level, rsc, text, nodes, data_set)
Definition: internal.h:353
#define pcmk__order_starts(rsc1, rsc2, type, data_set)
int priority
Definition: pe_types.h:408
pe_resource_t * container
Definition: pe_types.h:380
int(* message)(pcmk__output_t *out, const char *message_id,...)
enum rsc_role_e role
Definition: pe_types.h:370
GList * children
Definition: pe_types.h:377
int count
Definition: pe_types.h:243
resource_alloc_functions_t * cmds
Definition: pe_types.h:334
gboolean standby
Definition: pe_types.h:460
enum rsc_role_e next_role
Definition: pe_types.h:371
int char2score(const char *score)
Definition: utils.c:61
#define pcmk__config_err(fmt...)
pe_resource_t * remote_rsc
Definition: pe_types.h:230
pe_node_t * pcmk__set_instance_roles(pe_resource_t *rsc, pe_working_set_t *data_set)
#define pe_rsc_unique
Definition: pe_types.h:254
char * score2char_stack(int score, char *buf, size_t len)
Definition: utils.c:101
resource_object_functions_t * fns
Definition: pe_types.h:333
gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set)
pe_resource_t * dependent
void node_list_exclude(GHashTable *list, GList *list2, gboolean merge_scores)
Definition: utils.c:161
char * pcmk_promotion_score_name(const char *rsc_id)
Return the name of the node attribute used as a promotion score.
Definition: attrd_client.c:320
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:209
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:1691
GList * rsc_cons_lhs
Definition: pe_types.h:356
pe_ticket_t * ticket
enum crm_ais_msg_types type
Definition: cpg.c:48
#define RSC_START
Definition: crm.h:201
gboolean can_run_resources(const pe_node_t *node)
#define pe_flag_have_quorum
Definition: pe_types.h:94
const char * action
Definition: pcmk_fence.c:30
notify_data_t * create_notification_boundaries(pe_resource_t *rsc, const char *action, pe_action_t *start, pe_action_t *end, pe_working_set_t *data_set)
#define pe__set_resource_flags(resource, flags_to_set)
Definition: internal.h:47
#define pe_rsc_provisional
Definition: pe_types.h:258
const char * role2text(enum rsc_role_e role)
Definition: common.c:459
pe_node_t *(* location)(const pe_resource_t *, GList **, int)
Definition: pe_types.h:54
int weight
Definition: pe_types.h:241
#define pe_rsc_merging
Definition: pe_types.h:260
void create_promotable_actions(pe_resource_t *rsc, pe_working_set_t *data_set)
int rc
Definition: pcmk_fence.c:35
pe_action_t * create_pseudo_resource_op(pe_resource_t *rsc, const char *task, bool optional, bool runnable, pe_working_set_t *data_set)
pe_resource_t * uber_parent(pe_resource_t *rsc)
Definition: complex.c:903
void promotable_constraints(pe_resource_t *rsc, pe_working_set_t *data_set)
bool pe__is_guest_node(const pe_node_t *node)
Definition: remote.c:33
char * clone_strip(const char *last_rsc_id)
Definition: unpack.c:1628
void(* rsc_colocation_lh)(pe_resource_t *, pe_resource_t *, pcmk__colocation_t *, pe_working_set_t *)
pe_resource_t * primary
match only clone instances
Definition: pe_types.h:86
#define CRM_ATTR_UNAME
Definition: crm.h:114
#define crm_trace(fmt, args...)
Definition: logging.h:363
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:114
struct pe_node_shared_s * details
Definition: pe_types.h:244
void promote_demote_constraints(pe_resource_t *rsc, pe_working_set_t *data_set)
gboolean order_actions(pe_action_t *lh_action, pe_action_t *rh_action, enum pe_ordering order)
Definition: utils.c:1905
unsigned long long flags
Definition: pe_types.h:348
const char * uname
Definition: pe_types.h:209
#define pcmk__order_resource_actions(lh_rsc, lh_task, rh_rsc, rh_task, flags, data_set)
char * clone_name
Definition: pe_types.h:323
const char * pe_node_attribute_raw(pe_node_t *node, const char *name)
Definition: common.c:635
GList * actions
Definition: pe_types.h:359
gboolean granted
Definition: pe_types.h:458
#define RSC_DEMOTED
Definition: crm.h:210
const char * id
Definition: pe_types.h:208
void promotable_colocation_rh(pe_resource_t *dependent, pe_resource_t *primary, pcmk__colocation_t *constraint, pe_working_set_t *data_set)
pe_node_t * pe_find_node_id(GList *node_list, const char *id)
Definition: status.c:418
pe_resource_t * find_compatible_child(pe_resource_t *local_child, pe_resource_t *rsc, enum rsc_role_e filter, gboolean current, pe_working_set_t *data_set)
enum rsc_role_e(* state)(const pe_resource_t *, gboolean)
Definition: pe_types.h:53
#define RSC_STARTED
Definition: crm.h:202
int sort_index
Definition: pe_types.h:342
int pe__add_scores(int score1, int score2)
Definition: common.c:516
#define crm_err(fmt, args...)
Definition: logging.h:357
#define CRM_ASSERT(expr)
Definition: results.h:42
#define RSC_PROMOTE
Definition: crm.h:207
void pe__set_next_role(pe_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition: complex.c:1116
pe_weights
void pcmk__add_promotion_scores(pe_resource_t *rsc)
This structure contains everything that makes up a single output formatter.
GList * rsc_location
Definition: pe_types.h:358
#define pe__clear_resource_flags(resource, flags_to_clear)
Definition: internal.h:53
#define crm_str(x)
Definition: logging.h:383
rsc_role_e
Possible roles that a resource can be in.
Definition: common.h:92
GList * running_on
Definition: pe_types.h:366
void(* create_actions)(pe_resource_t *, pe_working_set_t *)
const char * node_attribute
#define RSC_STOPPED
Definition: crm.h:205
#define RSC_PROMOTED
Definition: crm.h:208
#define pe_rsc_trace(rsc, fmt, args...)
Definition: internal.h:20
unsigned long long flags
Definition: pe_types.h:146
char * name
Definition: pcmk_fence.c:31
pe_resource_t *(* find_rsc)(pe_resource_t *parent, const char *search, const pe_node_t *node, int flags)
Definition: pe_types.h:45
#define pe_flag_show_scores
Definition: pe_types.h:133
#define crm_info(fmt, args...)
Definition: logging.h:360
#define pe_rsc_managed
Definition: pe_types.h:249
pe_ordering
Definition: pe_types.h:483
uint64_t flags
Definition: remote.c:149
bool pcmk__is_daemon
Definition: logging.c:47
match resource active on specified node
Definition: pe_types.h:87
GHashTable *(* merge_weights)(pe_resource_t *, const char *, GHashTable *, const char *, float, enum pe_weights)
#define RSC_DEMOTE
Definition: crm.h:209
#define pe_rsc_info(rsc, fmt, args...)
Definition: internal.h:18
char * id
Definition: pe_types.h:322
GHashTable * allowed_nodes
Definition: pe_types.h:368