pacemaker  3.0.0-d8340737c4
Scalable High-Availability cluster resource manager
pcmk_sched_group.c
Go to the documentation of this file.
1 /*
2  * Copyright 2004-2024 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 <stdbool.h>
13 
14 #include <qb/qbdefs.h> // QB_ABS()
15 
16 #include <crm/common/xml.h>
17 
18 #include <pacemaker-internal.h>
19 #include "libpacemaker_private.h"
20 
41  bool stop_if_fail)
42 {
43  pcmk_node_t *first_assigned_node = NULL;
44  pcmk_resource_t *first_member = NULL;
45 
46  pcmk__assert(pcmk__is_group(rsc));
47 
49  return rsc->priv->assigned_node; // Assignment already done
50  }
52  pcmk__rsc_debug(rsc, "Assignment dependency loop detected involving %s",
53  rsc->id);
54  return NULL;
55  }
56 
57  if (rsc->priv->children == NULL) {
58  // No members to assign
60  return NULL;
61  }
62 
64  first_member = (pcmk_resource_t *) rsc->priv->children->data;
65  rsc->priv->orig_role = first_member->priv->orig_role;
66 
69  rsc, __func__, rsc->priv->allowed_nodes,
70  rsc->priv->scheduler);
71 
72  for (GList *iter = rsc->priv->children;
73  iter != NULL; iter = iter->next) {
74 
75  pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
76  pcmk_node_t *node = NULL;
77 
78  pcmk__rsc_trace(rsc, "Assigning group %s member %s",
79  rsc->id, member->id);
80  node = member->priv->cmds->assign(member, prefer, stop_if_fail);
81  if (first_assigned_node == NULL) {
82  first_assigned_node = node;
83  }
84  }
85 
86  pe__set_next_role(rsc, first_member->priv->next_role,
87  "first group member");
89 
91  return NULL;
92  }
93  return first_assigned_node;
94 }
95 
105 static pcmk_action_t *
106 create_group_pseudo_op(pcmk_resource_t *group, const char *action)
107 {
108  pcmk_action_t *op = custom_action(group, pcmk__op_key(group->id, action, 0),
109  action, NULL, TRUE,
110  group->priv->scheduler);
111 
113  return op;
114 }
115 
122 void
124 {
125  pcmk__assert(pcmk__is_group(rsc));
126 
127  pcmk__rsc_trace(rsc, "Creating actions for group %s", rsc->id);
128 
129  // Create actions for individual group members
130  for (GList *iter = rsc->priv->children;
131  iter != NULL; iter = iter->next) {
132 
133  pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
134 
135  member->priv->cmds->create_actions(member);
136  }
137 
138  // Create pseudo-actions for group itself to serve as ordering points
139  create_group_pseudo_op(rsc, PCMK_ACTION_START);
140  create_group_pseudo_op(rsc, PCMK_ACTION_RUNNING);
141  create_group_pseudo_op(rsc, PCMK_ACTION_STOP);
142  create_group_pseudo_op(rsc, PCMK_ACTION_STOPPED);
143  if (crm_is_true(g_hash_table_lookup(rsc->priv->meta,
145  create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTE);
146  create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTED);
147  create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTE);
148  create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTED);
149  }
150 }
151 
152 // User data for member_internal_constraints()
153 struct member_data {
154  // These could be derived from member but this avoids some function calls
155  bool ordered;
156  bool colocated;
157  bool promotable;
158 
159  pcmk_resource_t *last_active;
160  pcmk_resource_t *previous_member;
161 };
162 
170 static void
171 member_internal_constraints(gpointer data, gpointer user_data)
172 {
173  pcmk_resource_t *member = (pcmk_resource_t *) data;
174  struct member_data *member_data = (struct member_data *) user_data;
175 
176  // For ordering demote vs demote or stop vs stop
177  uint32_t down_flags = pcmk__ar_then_implies_first_graphed;
178 
179  // For ordering demote vs demoted or stop vs stopped
180  uint32_t post_down_flags = pcmk__ar_first_implies_then_graphed;
181 
182  // Create the individual member's implicit constraints
183  member->priv->cmds->internal_constraints(member);
184 
185  if (member_data->previous_member == NULL) {
186  // This is first member
187  if (member_data->ordered) {
189  post_down_flags = pcmk__ar_first_implies_then;
190  }
191 
192  } else if (member_data->colocated) {
193  uint32_t flags = pcmk__coloc_none;
194 
195  if (pcmk_is_set(member->flags, pcmk__rsc_critical)) {
197  }
198 
199  // Colocate this member with the previous one
200  pcmk__new_colocation("#group-members", NULL, PCMK_SCORE_INFINITY,
201  member, member_data->previous_member, NULL, NULL,
202  flags);
203  }
204 
205  if (member_data->promotable) {
206  // Demote group -> demote member -> group is demoted
209  member, PCMK_ACTION_DEMOTE, down_flags);
211  member->priv->parent,
212  PCMK_ACTION_DEMOTED, post_down_flags);
213 
214  // Promote group -> promote member -> group is promoted
216  member->priv->parent,
223  member, PCMK_ACTION_PROMOTE,
225  }
226 
227  // Stop group -> stop member -> group is stopped
228  pcmk__order_stops(member->priv->parent, member, down_flags);
230  member->priv->parent, PCMK_ACTION_STOPPED,
231  post_down_flags);
232 
233  // Start group -> start member -> group is started
234  pcmk__order_starts(member->priv->parent, member,
237  member->priv->parent, PCMK_ACTION_RUNNING,
241 
242  if (!member_data->ordered) {
243  pcmk__order_starts(member->priv->parent, member,
247  if (member_data->promotable) {
250  member, PCMK_ACTION_PROMOTE,
254  }
255 
256  } else if (member_data->previous_member == NULL) {
257  pcmk__order_starts(member->priv->parent, member, pcmk__ar_none);
258  if (member_data->promotable) {
261  member, PCMK_ACTION_PROMOTE,
262  pcmk__ar_none);
263  }
264 
265  } else {
266  // Order this member relative to the previous one
267 
268  pcmk__order_starts(member_data->previous_member, member,
271  pcmk__order_stops(member, member_data->previous_member,
273 
274  /* In unusual circumstances (such as adding a new member to the middle
275  * of a group with unmanaged later members), this member may be active
276  * while the previous (new) member is inactive. In this situation, the
277  * usual restart orderings will be irrelevant, so we need to order this
278  * member's stop before the previous member's start.
279  */
280  if ((member->priv->active_nodes != NULL)
281  && (member_data->previous_member->priv->active_nodes == NULL)) {
283  member_data->previous_member,
287  }
288 
289  if (member_data->promotable) {
290  pcmk__order_resource_actions(member_data->previous_member,
291  PCMK_ACTION_PROMOTE, member,
296  member_data->previous_member,
298  }
299  }
300 
301  // Make sure partially active groups shut down in sequence
302  if (member->priv->active_nodes != NULL) {
303  if (member_data->ordered && (member_data->previous_member != NULL)
304  && (member_data->previous_member->priv->active_nodes == NULL)
305  && (member_data->last_active != NULL)
306  && (member_data->last_active->priv->active_nodes != NULL)) {
307  pcmk__order_stops(member, member_data->last_active,
309  }
310  member_data->last_active = member;
311  }
312 
313  member_data->previous_member = member;
314 }
315 
322 void
324 {
325  struct member_data member_data = { false, };
326  const pcmk_resource_t *top = NULL;
327 
328  pcmk__assert(pcmk__is_group(rsc));
329 
330  /* Order group pseudo-actions relative to each other for restarting:
331  * stop group -> group is stopped -> start group -> group is started
332  */
334  rsc, PCMK_ACTION_STOPPED,
337  rsc, PCMK_ACTION_START,
340  rsc, PCMK_ACTION_RUNNING,
342 
343  top = pe__const_top_resource(rsc, false);
344 
345  member_data.ordered = pe__group_flag_is_set(rsc, pcmk__group_ordered);
346  member_data.colocated = pe__group_flag_is_set(rsc, pcmk__group_colocated);
347  member_data.promotable = pcmk_is_set(top->flags, pcmk__rsc_promotable);
348  g_list_foreach(rsc->priv->children, member_internal_constraints,
349  &member_data);
350 }
351 
366 static int
367 colocate_group_with(pcmk_resource_t *dependent, const pcmk_resource_t *primary,
368  const pcmk__colocation_t *colocation)
369 {
370  int priority_delta = 0;
371 
372  if (dependent->priv->children == NULL) {
373  return 0;
374  }
375 
376  pcmk__rsc_trace(primary, "Processing %s (group %s with %s) for dependent",
377  colocation->id, dependent->id, primary->id);
378 
380  // Colocate first member (internal colocations will handle the rest)
381  pcmk_resource_t *member = dependent->priv->children->data;
382  priority_delta = member->priv->cmds->apply_coloc_score(member, primary,
383  colocation,
384  true);
385 
386  } else {
387  if (colocation->score >= PCMK_SCORE_INFINITY) {
388  pcmk__config_err("%s: Cannot perform mandatory colocation between "
389  "non-colocated group and %s",
390  dependent->id, primary->id);
391  return 0;
392  }
393 
394  // Colocate each member individually
395  for (GList *iter = dependent->priv->children; iter != NULL;
396  iter = iter->next) {
397 
398  int instance_delta = 0;
399  pcmk_resource_t *member = iter->data;
400 
401  instance_delta =
402  member->priv->cmds->apply_coloc_score(member, primary,
403  colocation, false);
404 
405  /* priority_delta is used for determining which instances of a
406  * promotable clone to promote. It's possible that colocations
407  * involving promotable cloned non-colocated groups may not behave
408  * correctly in all circumstances. Non-colocated groups are
409  * deprecated, and testing focused on colocated groups.
410  */
411  priority_delta = pcmk__add_scores(priority_delta, instance_delta);
412  }
413  }
414 
415  if (priority_delta != 0) {
416  dependent->priv->priority =
417  pcmk__add_scores(priority_delta, dependent->priv->priority);
418 
419  pcmk__rsc_trace(dependent,
420  "Applied %s to %s promotion priority "
421  "(now %s after %s %d)",
422  colocation->id, dependent->id,
423  pcmk_readable_score(dependent->priv->priority),
424  ((priority_delta > 0)? "adding" : "subtracting"),
425  QB_ABS(priority_delta));
426  }
427  return priority_delta;
428 }
429 
444 static int
445 colocate_with_group(pcmk_resource_t *dependent, const pcmk_resource_t *primary,
446  const pcmk__colocation_t *colocation)
447 {
448  int priority_delta = 0;
449  const pcmk_resource_t *member = NULL;
450 
451  pcmk__rsc_trace(primary,
452  "Processing colocation %s (%s with group %s) for primary",
453  colocation->id, dependent->id, primary->id);
454 
455  if (pcmk_is_set(primary->flags, pcmk__rsc_unassigned)) {
456  return 0;
457  }
458 
460 
461  if (colocation->score >= PCMK_SCORE_INFINITY) {
462  /* For mandatory colocations, the entire group must be assignable
463  * (and in the specified role if any), so apply the colocation based
464  * on the last member.
465  */
466  member = pe__last_group_member(primary);
467  } else if (primary->priv->children != NULL) {
468  /* For optional colocations, whether the group is partially or fully
469  * up doesn't matter, so apply the colocation based on the first
470  * member.
471  */
472  member = (pcmk_resource_t *) primary->priv->children->data;
473  }
474  if (member == NULL) {
475  return 0; // Nothing to colocate with
476  }
477 
478  return member->priv->cmds->apply_coloc_score(dependent, member,
479  colocation, false);
480  }
481 
482  if (colocation->score >= PCMK_SCORE_INFINITY) {
483  pcmk__config_err("%s: Cannot perform mandatory colocation with"
484  " non-colocated group %s",
485  dependent->id, primary->id);
486  return 0;
487  }
488 
489  // Colocate dependent with each member individually
490  for (const GList *iter = primary->priv->children;
491  iter != NULL; iter = iter->next) {
492 
493  int instance_delta = 0;
494 
495  member = iter->data;
496  instance_delta = member->priv->cmds->apply_coloc_score(dependent,
497  member,
498  colocation,
499  false);
500  priority_delta = pcmk__add_scores(priority_delta, instance_delta);
501  }
502  return priority_delta;
503 }
504 
520 int
522  const pcmk_resource_t *primary,
523  const pcmk__colocation_t *colocation,
524  bool for_dependent)
525 {
526  pcmk__assert((dependent != NULL) && (primary != NULL)
527  && (colocation != NULL));
528 
529  if (for_dependent) {
530  return colocate_group_with(dependent, primary, colocation);
531 
532  } else {
533  // Method should only be called for primitive dependents
534  pcmk__assert(pcmk__is_primitive(dependent));
535 
536  return colocate_with_group(dependent, primary, colocation);
537  }
538 }
539 
549 uint32_t
551 {
552  // Default flags for a group action
553  uint32_t flags = pcmk__action_optional
556 
557  pcmk__assert(action != NULL);
558 
559  // Update flags considering each member's own flags for same action
560  for (GList *iter = action->rsc->priv->children;
561  iter != NULL; iter = iter->next) {
562 
563  pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
564 
565  // Check whether member has the same action
566  enum pcmk__action_type task = get_complex_task(member, action->task);
567  const char *task_s = pcmk__action_text(task);
568  pcmk_action_t *member_action = NULL;
569 
570  member_action = find_first_action(member->priv->actions, NULL,
571  task_s, node);
572  if (member_action != NULL) {
573  uint32_t member_flags = 0U;
574 
575  member_flags = member->priv->cmds->action_flags(member_action,
576  node);
577 
578  // Group action is mandatory if any member action is
580  && !pcmk_is_set(member_flags, pcmk__action_optional)) {
581  pcmk__rsc_trace(action->rsc, "%s is mandatory because %s is",
582  action->uuid, member_action->uuid);
583  pcmk__clear_raw_action_flags(flags, "group action",
586  }
587 
588  // Group action is unrunnable if any member action is
589  if (!pcmk__str_eq(task_s, action->task, pcmk__str_none)
591  && !pcmk_is_set(member_flags, pcmk__action_runnable)) {
592 
593  pcmk__rsc_trace(action->rsc, "%s is unrunnable because %s is",
594  action->uuid, member_action->uuid);
595  pcmk__clear_raw_action_flags(flags, "group action",
598  }
599 
600  /* Group (pseudo-)actions other than stop or demote are unrunnable
601  * unless every member will do it.
602  */
603  } else if ((task != pcmk__action_stop)
604  && (task != pcmk__action_demote)) {
605  pcmk__rsc_trace(action->rsc,
606  "%s is not runnable because %s will not %s",
607  action->uuid, member->id, task_s);
608  pcmk__clear_raw_action_flags(flags, "group action",
610  }
611  }
612 
613  return flags;
614 }
615 
638 uint32_t
640  const pcmk_node_t *node, uint32_t flags,
641  uint32_t filter, uint32_t type,
643 {
644  uint32_t changed = pcmk__updated_none;
645 
646  // Group method can be called only on behalf of "then" action
647  pcmk__assert((first != NULL) && (then != NULL) && (then->rsc != NULL)
648  && (scheduler != NULL));
649 
650  // Update the actions for the group itself
651  changed |= pcmk__update_ordered_actions(first, then, node, flags, filter,
652  type, scheduler);
653 
654  // Update the actions for each group member
655  for (GList *iter = then->rsc->priv->children;
656  iter != NULL; iter = iter->next) {
657 
658  pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
659  pcmk_action_t *member_action = NULL;
660 
661  member_action = find_first_action(member->priv->actions, NULL,
662  then->task, node);
663  if (member_action == NULL) {
664  continue;
665  }
666  changed |= member->priv->cmds->update_ordered_actions(first,
667  member_action,
668  node, flags,
669  filter, type,
670  scheduler);
671  }
672  return changed;
673 }
674 
682 void
684 {
685  GList *node_list_orig = NULL;
686  GList *node_list_copy = NULL;
687 
688  pcmk__assert(pcmk__is_group(rsc) && (location != NULL));
689 
690  // Save the constraint's original node list (with the constraint score)
691  node_list_orig = location->nodes;
692 
693  // Make a copy of the nodes with all zero scores
694  node_list_copy = pcmk__copy_node_list(node_list_orig, true);
695 
696  /* Apply the constraint to the group itself. This ensures that any nodes
697  * affected by the constraint are in the group's allowed nodes, with the
698  * constraint score added.
699  */
700  pcmk__apply_location(rsc, location);
701 
702  // Apply the constraint for each member
703  for (GList *iter = rsc->priv->children;
704  iter != NULL; iter = iter->next) {
705 
706  pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
707 
709  && (iter != rsc->priv->children)) {
710  /* When apply_location() is called below for the first member (iter
711  * == rsc->priv->children), the constraint score will be added to
712  * the member's affected allowed nodes.
713  *
714  * For subsequent members, we reset the constraint's node table to
715  * the copy with all 0 scores. Otherwise, when assigning the member,
716  * the constraint score would be counted multiple times (once for
717  * each later member) due to internal group colocations. Though the
718  * 0 score will not affect these members' allowed node scores, it
719  * ensures that affected nodes are in each member's allowed nodes,
720  * enabling the member on those nodes in asymmetric clusters.
721  */
722  location->nodes = node_list_copy;
723  }
724 
725  member->priv->cmds->apply_location(member, location);
726  }
727 
728  location->nodes = node_list_orig;
729  g_list_free_full(node_list_copy, free);
730 }
731 
732 // Group implementation of pcmk__assignment_methods_t:colocated_resources()
733 GList *
735  const pcmk_resource_t *orig_rsc,
736  GList *colocated_rscs)
737 {
738  pcmk__assert(pcmk__is_group(rsc));
739 
740  if (orig_rsc == NULL) {
741  orig_rsc = rsc;
742  }
743 
745  || pcmk__is_clone(rsc->priv->parent)) {
746  /* This group has colocated members and/or is cloned -- either way,
747  * add every child's colocated resources to the list. The first and last
748  * members will include the group's own colocations.
749  */
750  colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
751 
752  for (const GList *iter = rsc->priv->children;
753  iter != NULL; iter = iter->next) {
754 
755  const pcmk_resource_t *member = iter->data;
756 
757  colocated_rscs = member->priv->cmds->colocated_resources(member,
758  orig_rsc,
759  colocated_rscs);
760  }
761 
762  } else if (rsc->priv->children != NULL) {
763  /* This group's members are not colocated, and the group is not cloned,
764  * so just add the group's own colocations to the list.
765  */
766  colocated_rscs = pcmk__colocated_resources(rsc, orig_rsc,
767  colocated_rscs);
768  }
769 
770  return colocated_rscs;
771 }
772 
773 // Group implementation of pcmk__assignment_methods_t:with_this_colocations()
774 void
776  const pcmk_resource_t *orig_rsc, GList **list)
777 
778 {
779  const pcmk_resource_t *parent = NULL;
780 
781  pcmk__assert((orig_rsc != NULL) && (list != NULL) && pcmk__is_group(rsc));
782  parent = rsc->priv->parent;
783 
784  // Ignore empty groups
785  if (rsc->priv->children == NULL) {
786  return;
787  }
788 
789  /* "With this" colocations are needed only for the group itself and for its
790  * last member. (Previous members will chain via the group internal
791  * colocations.)
792  */
793  if ((orig_rsc != rsc) && (orig_rsc != pe__last_group_member(rsc))) {
794  return;
795  }
796 
797  pcmk__rsc_trace(rsc, "Adding 'with %s' colocations to list for %s",
798  rsc->id, orig_rsc->id);
799 
800  // Add the group's own colocations
802  orig_rsc);
803 
804  // If cloned, add any relevant colocations with the clone
805  if (parent != NULL) {
806  parent->priv->cmds->with_this_colocations(parent, orig_rsc, list);
807  }
808 
810  // @COMPAT Non-colocated groups are deprecated
811  return;
812  }
813 
814  // Add explicit colocations with the group's (other) children
815  for (const GList *iter = rsc->priv->children;
816  iter != NULL; iter = iter->next) {
817 
818  const pcmk_resource_t *member = iter->data;
819 
820  if (member == orig_rsc) {
821  continue;
822  }
823  member->priv->cmds->with_this_colocations(member, orig_rsc, list);
824  }
825 }
826 
827 // Group implementation of pcmk__assignment_methods_t:this_with_colocations()
828 void
830  const pcmk_resource_t *orig_rsc, GList **list)
831 {
832  const pcmk_resource_t *parent = NULL;
833  const pcmk_resource_t *member = NULL;
834 
835  pcmk__assert((orig_rsc != NULL) && (list != NULL) && pcmk__is_group(rsc));
836  parent = rsc->priv->parent;
837 
838  // Ignore empty groups
839  if (rsc->priv->children == NULL) {
840  return;
841  }
842 
843  /* "This with" colocations are normally needed only for the group itself and
844  * for its first member.
845  */
846  if ((rsc == orig_rsc) || (orig_rsc == rsc->priv->children->data)) {
847  pcmk__rsc_trace(rsc, "Adding '%s with' colocations to list for %s",
848  rsc->id, orig_rsc->id);
849 
850  // Add the group's own colocations
852  orig_rsc);
853 
854  // If cloned, add any relevant colocations involving the clone
855  if (parent != NULL) {
856  parent->priv->cmds->this_with_colocations(parent, orig_rsc, list);
857  }
858 
860  // @COMPAT Non-colocated groups are deprecated
861  return;
862  }
863 
864  // Add explicit colocations involving the group's (other) children
865  for (const GList *iter = rsc->priv->children;
866  iter != NULL; iter = iter->next) {
867 
868  member = iter->data;
869  if (member == orig_rsc) {
870  continue;
871  }
872  member->priv->cmds->this_with_colocations(member, orig_rsc, list);
873  }
874  return;
875  }
876 
877  /* Later group members honor the group's colocations indirectly, due to the
878  * internal group colocations that chain everything from the first member.
879  * However, if an earlier group member is unmanaged, this chaining will not
880  * happen, so the group's mandatory colocations must be explicitly added.
881  */
882  for (const GList *iter = rsc->priv->children;
883  iter != NULL; iter = iter->next) {
884 
885  member = iter->data;
886  if (orig_rsc == member) {
887  break; // We've seen all earlier members, and none are unmanaged
888  }
889 
890  if (!pcmk_is_set(member->flags, pcmk__rsc_managed)) {
891  crm_trace("Adding mandatory '%s with' colocations to list for "
892  "member %s because earlier member %s is unmanaged",
893  rsc->id, orig_rsc->id, member->id);
894  for (const GList *cons_iter = rsc->priv->this_with_colocations;
895  cons_iter != NULL; cons_iter = cons_iter->next) {
896  const pcmk__colocation_t *colocation = NULL;
897 
898  colocation = (const pcmk__colocation_t *) cons_iter->data;
899  if (colocation->score == PCMK_SCORE_INFINITY) {
900  pcmk__add_this_with(list, colocation, orig_rsc);
901  }
902  }
903  // @TODO Add mandatory (or all?) clone constraints if cloned
904  break;
905  }
906  }
907 }
908 
939 void
941  const pcmk_resource_t *target_rsc,
942  const char *log_id, GHashTable **nodes,
943  const pcmk__colocation_t *colocation,
944  float factor, uint32_t flags)
945 {
946  pcmk_resource_t *member = NULL;
947 
948  pcmk__assert(pcmk__is_group(source_rsc) && (nodes != NULL)
949  && ((colocation != NULL)
950  || ((target_rsc == NULL) && (*nodes == NULL))));
951 
952  if (log_id == NULL) {
953  log_id = source_rsc->id;
954  }
955 
956  // Avoid infinite recursion
957  if (pcmk_is_set(source_rsc->flags, pcmk__rsc_updating_nodes)) {
958  pcmk__rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
959  log_id, source_rsc->id);
960  return;
961  }
963 
964  // Ignore empty groups (only possible with schema validation disabled)
965  if (source_rsc->priv->children == NULL) {
966  return;
967  }
968 
969  /* Refer the operation to the first or last member as appropriate.
970  *
971  * cmp_resources() is the only caller that passes a NULL nodes table,
972  * and is also the only caller using pcmk__coloc_select_this_with.
973  * For "this with" colocations, the last member will recursively incorporate
974  * all the other members' "this with" colocations via the internal group
975  * colocations (and via the first member, the group's own colocations).
976  *
977  * For "with this" colocations, the first member works similarly.
978  */
979  if (*nodes == NULL) {
980  member = pe__last_group_member(source_rsc);
981  } else {
982  member = source_rsc->priv->children->data;
983  }
984 
985  pcmk__rsc_trace(source_rsc, "%s: Merging scores from group %s using member %s "
986  "(at %.6f)", log_id, source_rsc->id, member->id, factor);
987  member->priv->cmds->add_colocated_node_scores(member, target_rsc, log_id,
988  nodes, colocation, factor,
989  flags);
991 }
992 
993 // Group implementation of pcmk__assignment_methods_t:add_utilization()
994 void
996  const pcmk_resource_t *orig_rsc, GList *all_rscs,
997  GHashTable *utilization)
998 {
999  pcmk_resource_t *member = NULL;
1000 
1001  pcmk__assert((orig_rsc != NULL) && (utilization != NULL)
1002  && pcmk__is_group(rsc));
1003 
1004  if (!pcmk_is_set(rsc->flags, pcmk__rsc_unassigned)) {
1005  return;
1006  }
1007 
1008  pcmk__rsc_trace(orig_rsc, "%s: Adding group %s as colocated utilization",
1009  orig_rsc->id, rsc->id);
1011  || pcmk__is_clone(rsc->priv->parent)) {
1012 
1013  // Every group member will be on same node, so sum all members
1014  for (GList *iter = rsc->priv->children;
1015  iter != NULL; iter = iter->next) {
1016 
1017  member = (pcmk_resource_t *) iter->data;
1018 
1019  if (pcmk_is_set(member->flags, pcmk__rsc_unassigned)
1020  && (g_list_find(all_rscs, member) == NULL)) {
1021  member->priv->cmds->add_utilization(member, orig_rsc, all_rscs,
1022  utilization);
1023  }
1024  }
1025 
1026  } else if (rsc->priv->children != NULL) {
1027  // Just add first member's utilization
1028  member = (pcmk_resource_t *) rsc->priv->children->data;
1029  if ((member != NULL)
1031  && (g_list_find(all_rscs, member) == NULL)) {
1032 
1033  member->priv->cmds->add_utilization(member, orig_rsc, all_rscs,
1034  utilization);
1035  }
1036  }
1037 }
1038 
1039 void
1041 {
1042  pcmk__assert(pcmk__is_group(rsc));
1043 
1044  for (GList *iter = rsc->priv->children;
1045  iter != NULL; iter = iter->next) {
1046 
1047  pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
1048 
1049  member->priv->cmds->shutdown_lock(member);
1050  }
1051 }
const pcmk_resource_t * pe__const_top_resource(const pcmk_resource_t *rsc, bool include_bundle)
Definition: complex.c:1043
void pcmk__group_create_actions(pcmk_resource_t *rsc)
pcmk_node_t * pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail)
G_GNUC_INTERNAL GList * pcmk__colocated_resources(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs)
pcmk__action_type
#define pcmk__order_starts(rsc1, rsc2, flags)
void pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
&#39;then&#39; is runnable (and migratable) only if &#39;first&#39; is runnable
G_GNUC_INTERNAL void pcmk__apply_location(pcmk_resource_t *rsc, pcmk__location_t *constraint)
#define PCMK_META_PROMOTABLE
Definition: options.h:101
char data[0]
Definition: cpg.c:58
uint32_t(* update_ordered_actions)(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
void(* create_actions)(pcmk_resource_t *rsc)
int pcmk__group_apply_coloc_score(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent)
pcmk_resource_t * parent
enum pcmk_ipc_server type
Definition: cpg.c:51
const char * pcmk_readable_score(int score)
Return a displayable static string for a score value.
Definition: scores.c:102
pcmk_node_t *(* assign)(pcmk_resource_t *rsc, const pcmk_node_t *prefer, bool stop_if_fail)
#define pcmk__rsc_trace(rsc, fmt, args...)
#define pcmk__order_stops(rsc1, rsc2, flags)
#define pcmk__rsc_info(rsc, fmt, args...)
#define pcmk__set_rsc_flags(resource, flags_to_set)
#define pcmk__config_err(fmt...)
void(* add_utilization)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization)
uint64_t flags
Definition: scheduler.h:89
void(* with_this_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
G_GNUC_INTERNAL uint32_t pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
void(* this_with_colocations)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
enum pcmk__action_type get_complex_task(const pcmk_resource_t *rsc, const char *name)
Definition: pe_actions.c:1383
#define pcmk__set_relation_flags(ar_flags, flags_to_set)
void(* internal_constraints)(pcmk_resource_t *rsc)
uint32_t pcmk__group_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then, const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type, pcmk_scheduler_t *scheduler)
const char * action
Definition: pcmk_fence.c:32
#define pcmk__rsc_debug(rsc, fmt, args...)
#define PCMK_ACTION_DEMOTE
Definition: actions.h:40
void pe__set_next_role(pcmk_resource_t *rsc, enum rsc_role_e role, const char *why)
Definition: complex.c:1265
G_GNUC_INTERNAL void pcmk__add_with_this_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
pcmk_scheduler_t * scheduler
uint32_t pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
Actions are ordered (optionally, if no other flags are set)
#define pcmk__clear_action_flags(action, flags_to_clear)
pcmk_resource_t * pe__last_group_member(const pcmk_resource_t *group)
Definition: group.c:37
G_GNUC_INTERNAL void pcmk__add_this_with(GList **list, const pcmk__colocation_t *colocation, const pcmk_resource_t *rsc)
#define crm_trace(fmt, args...)
Definition: logging.h:372
G_GNUC_INTERNAL void pcmk__new_colocation(const char *id, const char *node_attr, int score, pcmk_resource_t *dependent, pcmk_resource_t *primary, const char *dependent_role_spec, const char *primary_role_spec, uint32_t flags)
void(* add_colocated_node_scores)(pcmk_resource_t *source_rsc, const pcmk_resource_t *target_rsc, const char *log_id, GHashTable **nodes, const pcmk__colocation_t *colocation, float factor, uint32_t flags)
#define pcmk_is_set(g, f)
Convenience alias for pcmk_all_flags_set(), to check single flag.
Definition: util.h:80
void(* apply_location)(pcmk_resource_t *rsc, pcmk__location_t *location)
#define PCMK_ACTION_START
Definition: actions.h:63
pcmk__resource_private_t * priv
Definition: resources.h:61
void(* shutdown_lock)(pcmk_resource_t *rsc)
Wrappers for and extensions to libxml2.
GList * pcmk__group_colocated_resources(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs)
#define PCMK_ACTION_STOP
Definition: actions.h:66
#define pcmk__clear_rsc_flags(resource, flags_to_clear)
void pcmk__group_with_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
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:195
int pcmk__add_scores(int score1, int score2)
Definition: scores.c:159
#define pcmk__order_resource_actions(first_rsc, first_task, then_rsc, then_task, flags)
pcmk_action_t * custom_action(pcmk_resource_t *rsc, char *key, const char *task, const pcmk_node_t *on_node, gboolean optional, pcmk_scheduler_t *scheduler)
Create or update an action object.
Definition: pe_actions.c:1093
void pcmk__group_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
#define pcmk__assert(expr)
uint32_t(* action_flags)(pcmk_action_t *action, const pcmk_node_t *node)
If &#39;then&#39; is required, &#39;first&#39; must be added to the transition graph.
GList * pcmk__copy_node_list(const GList *list, bool reset)
pcmk_action_t * find_first_action(const GList *input, const char *uuid, const char *task, const pcmk_node_t *on_node)
Definition: pe_actions.c:1416
pcmk_resource_t * rsc
pcmk_scheduler_t * scheduler
G_GNUC_INTERNAL void pcmk__add_this_with_list(GList **list, GList *addition, const pcmk_resource_t *rsc)
If &#39;first&#39; is required and runnable, &#39;then&#39; must be in graph.
#define PCMK_ACTION_STOPPED
Definition: actions.h:67
void pcmk__group_add_utilization(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *all_rscs, GHashTable *utilization)
void pcmk__group_shutdown_lock(pcmk_resource_t *rsc)
#define PCMK_ACTION_PROMOTE
Definition: actions.h:57
void pcmk__with_group_colocations(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList **list)
gboolean crm_is_true(const char *s)
Definition: strings.c:490
#define pe__show_node_scores(level, rsc, text, nodes, scheduler)
Definition: internal.h:164
const char * pcmk__action_text(enum pcmk__action_type action)
Definition: actions.c:34
#define pcmk__set_action_flags(action, flags_to_set)
unsigned long long flags
Definition: resources.h:69
#define PCMK_ACTION_PROMOTED
Definition: actions.h:58
GList *(* colocated_resources)(const pcmk_resource_t *rsc, const pcmk_resource_t *orig_rsc, GList *colocated_rscs)
Location constraint object.
#define PCMK_ACTION_RUNNING
Definition: actions.h:62
const char * parent
Definition: cib.c:27
#define pcmk__clear_raw_action_flags(action_flags, action_name, to_clear)
bool pe__group_flag_is_set(const pcmk_resource_t *group, uint32_t flags)
Definition: group.c:59
#define PCMK_ACTION_DEMOTED
Definition: actions.h:41
int(* apply_coloc_score)(pcmk_resource_t *dependent, const pcmk_resource_t *primary, const pcmk__colocation_t *colocation, bool for_dependent)
void pcmk__group_internal_constraints(pcmk_resource_t *rsc)
uint64_t flags
Definition: remote.c:211
const pcmk__assignment_methods_t * cmds
No relation (compare with equality rather than bit set)
#define PCMK_SCORE_INFINITY
Integer score to use to represent "infinity".
Definition: scores.h:26