root/lib/pacemaker/pcmk_sched_group.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pcmk__group_assign
  2. create_group_pseudo_op
  3. pcmk__group_create_actions
  4. member_internal_constraints
  5. pcmk__group_internal_constraints
  6. colocate_group_with
  7. colocate_with_group
  8. pcmk__group_apply_coloc_score
  9. pcmk__group_action_flags
  10. pcmk__group_update_ordered_actions
  11. pcmk__group_apply_location
  12. pcmk__group_colocated_resources
  13. pcmk__with_group_colocations
  14. pcmk__group_with_colocations
  15. pcmk__group_add_colocated_node_scores
  16. pcmk__group_add_utilization
  17. pcmk__group_shutdown_lock

   1 /*
   2  * Copyright 2004-2023 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 <crm/msg_xml.h>
  15 
  16 #include <pacemaker-internal.h>
  17 #include "libpacemaker_private.h"
  18 
  19 /*!
  20  * \internal
  21  * \brief Assign a group resource to a node
  22  *
  23  * \param[in,out] rsc           Group resource to assign to a node
  24  * \param[in]     prefer        Node to prefer, if all else is equal
  25  * \param[in]     stop_if_fail  If \c true and a child of \p rsc can't be
  26  *                              assigned to a node, set the child's next role to
  27  *                              stopped and update existing actions
  28  *
  29  * \return Node that \p rsc is assigned to, if assigned entirely to one node
  30  *
  31  * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
  32  *       completely undo the assignment. A successful assignment can be either
  33  *       undone or left alone as final. A failed assignment has the same effect
  34  *       as calling pcmk__unassign_resource(); there are no side effects on
  35  *       roles or actions.
  36  */
  37 pcmk_node_t *
  38 pcmk__group_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
     /* [previous][next][first][last][top][bottom][index][help] */
  39                    bool stop_if_fail)
  40 {
  41     pcmk_node_t *first_assigned_node = NULL;
  42     pcmk_resource_t *first_member = NULL;
  43 
  44     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
  45 
  46     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
  47         return rsc->allocated_to; // Assignment already done
  48     }
  49     if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) {
  50         pe_rsc_debug(rsc, "Assignment dependency loop detected involving %s",
  51                      rsc->id);
  52         return NULL;
  53     }
  54 
  55     if (rsc->children == NULL) {
  56         // No members to assign
  57         pe__clear_resource_flags(rsc, pcmk_rsc_unassigned);
  58         return NULL;
  59     }
  60 
  61     pe__set_resource_flags(rsc, pcmk_rsc_assigning);
  62     first_member = (pcmk_resource_t *) rsc->children->data;
  63     rsc->role = first_member->role;
  64 
  65     pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags,
  66                                       pcmk_sched_output_scores),
  67                          rsc, __func__, rsc->allowed_nodes, rsc->cluster);
  68 
  69     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
  70         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
  71         pcmk_node_t *node = NULL;
  72 
  73         pe_rsc_trace(rsc, "Assigning group %s member %s",
  74                      rsc->id, member->id);
  75         node = member->cmds->assign(member, prefer, stop_if_fail);
  76         if (first_assigned_node == NULL) {
  77             first_assigned_node = node;
  78         }
  79     }
  80 
  81     pe__set_next_role(rsc, first_member->next_role, "first group member");
  82     pe__clear_resource_flags(rsc, pcmk_rsc_assigning|pcmk_rsc_unassigned);
  83 
  84     if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) {
  85         return NULL;
  86     }
  87     return first_assigned_node;
  88 }
  89 
  90 /*!
  91  * \internal
  92  * \brief Create a pseudo-operation for a group as an ordering point
  93  *
  94  * \param[in,out] group   Group resource to create action for
  95  * \param[in]     action  Action name
  96  *
  97  * \return Newly created pseudo-operation
  98  */
  99 static pcmk_action_t *
 100 create_group_pseudo_op(pcmk_resource_t *group, const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102     pcmk_action_t *op = custom_action(group, pcmk__op_key(group->id, action, 0),
 103                                       action, NULL, TRUE, group->cluster);
 104     pe__set_action_flags(op, pcmk_action_pseudo|pcmk_action_runnable);
 105     return op;
 106 }
 107 
 108 /*!
 109  * \internal
 110  * \brief Create all actions needed for a given group resource
 111  *
 112  * \param[in,out] rsc  Group resource to create actions for
 113  */
 114 void
 115 pcmk__group_create_actions(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
 118 
 119     pe_rsc_trace(rsc, "Creating actions for group %s", rsc->id);
 120 
 121     // Create actions for individual group members
 122     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 123         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
 124 
 125         member->cmds->create_actions(member);
 126     }
 127 
 128     // Create pseudo-actions for group itself to serve as ordering points
 129     create_group_pseudo_op(rsc, PCMK_ACTION_START);
 130     create_group_pseudo_op(rsc, PCMK_ACTION_RUNNING);
 131     create_group_pseudo_op(rsc, PCMK_ACTION_STOP);
 132     create_group_pseudo_op(rsc, PCMK_ACTION_STOPPED);
 133     if (crm_is_true(g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_PROMOTABLE))) {
 134         create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTE);
 135         create_group_pseudo_op(rsc, PCMK_ACTION_DEMOTED);
 136         create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTE);
 137         create_group_pseudo_op(rsc, PCMK_ACTION_PROMOTED);
 138     }
 139 }
 140 
 141 // User data for member_internal_constraints()
 142 struct member_data {
 143     // These could be derived from member but this avoids some function calls
 144     bool ordered;
 145     bool colocated;
 146     bool promotable;
 147 
 148     pcmk_resource_t *last_active;
 149     pcmk_resource_t *previous_member;
 150 };
 151 
 152 /*!
 153  * \internal
 154  * \brief Create implicit constraints needed for a group member
 155  *
 156  * \param[in,out] data       Group member to create implicit constraints for
 157  * \param[in,out] user_data  Member data (struct member_data *)
 158  */
 159 static void
 160 member_internal_constraints(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 161 {
 162     pcmk_resource_t *member = (pcmk_resource_t *) data;
 163     struct member_data *member_data = (struct member_data *) user_data;
 164 
 165     // For ordering demote vs demote or stop vs stop
 166     uint32_t down_flags = pcmk__ar_then_implies_first_graphed;
 167 
 168     // For ordering demote vs demoted or stop vs stopped
 169     uint32_t post_down_flags = pcmk__ar_first_implies_then_graphed;
 170 
 171     // Create the individual member's implicit constraints
 172     member->cmds->internal_constraints(member);
 173 
 174     if (member_data->previous_member == NULL) {
 175         // This is first member
 176         if (member_data->ordered) {
 177             pe__set_order_flags(down_flags, pcmk__ar_ordered);
 178             post_down_flags = pcmk__ar_first_implies_then;
 179         }
 180 
 181     } else if (member_data->colocated) {
 182         uint32_t flags = pcmk__coloc_none;
 183 
 184         if (pcmk_is_set(member->flags, pcmk_rsc_critical)) {
 185             flags |= pcmk__coloc_influence;
 186         }
 187 
 188         // Colocate this member with the previous one
 189         pcmk__new_colocation("#group-members", NULL, INFINITY, member,
 190                              member_data->previous_member, NULL, NULL, flags);
 191     }
 192 
 193     if (member_data->promotable) {
 194         // Demote group -> demote member -> group is demoted
 195         pcmk__order_resource_actions(member->parent, PCMK_ACTION_DEMOTE,
 196                                      member, PCMK_ACTION_DEMOTE, down_flags);
 197         pcmk__order_resource_actions(member, PCMK_ACTION_DEMOTE,
 198                                      member->parent, PCMK_ACTION_DEMOTED,
 199                                      post_down_flags);
 200 
 201         // Promote group -> promote member -> group is promoted
 202         pcmk__order_resource_actions(member, PCMK_ACTION_PROMOTE,
 203                                      member->parent, PCMK_ACTION_PROMOTED,
 204                                      pcmk__ar_unrunnable_first_blocks
 205                                      |pcmk__ar_first_implies_then
 206                                      |pcmk__ar_first_implies_then_graphed);
 207         pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE,
 208                                      member, PCMK_ACTION_PROMOTE,
 209                                      pcmk__ar_then_implies_first_graphed);
 210     }
 211 
 212     // Stop group -> stop member -> group is stopped
 213     pcmk__order_stops(member->parent, member, down_flags);
 214     pcmk__order_resource_actions(member, PCMK_ACTION_STOP,
 215                                  member->parent, PCMK_ACTION_STOPPED,
 216                                  post_down_flags);
 217 
 218     // Start group -> start member -> group is started
 219     pcmk__order_starts(member->parent, member,
 220                        pcmk__ar_then_implies_first_graphed);
 221     pcmk__order_resource_actions(member, PCMK_ACTION_START,
 222                                  member->parent, PCMK_ACTION_RUNNING,
 223                                  pcmk__ar_unrunnable_first_blocks
 224                                  |pcmk__ar_first_implies_then
 225                                  |pcmk__ar_first_implies_then_graphed);
 226 
 227     if (!member_data->ordered) {
 228         pcmk__order_starts(member->parent, member,
 229                            pcmk__ar_first_implies_then
 230                            |pcmk__ar_unrunnable_first_blocks
 231                            |pcmk__ar_then_implies_first_graphed);
 232         if (member_data->promotable) {
 233             pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE,
 234                                          member, PCMK_ACTION_PROMOTE,
 235                                          pcmk__ar_first_implies_then
 236                                          |pcmk__ar_unrunnable_first_blocks
 237                                          |pcmk__ar_then_implies_first_graphed);
 238         }
 239 
 240     } else if (member_data->previous_member == NULL) {
 241         pcmk__order_starts(member->parent, member, pcmk__ar_none);
 242         if (member_data->promotable) {
 243             pcmk__order_resource_actions(member->parent, PCMK_ACTION_PROMOTE,
 244                                          member, PCMK_ACTION_PROMOTE,
 245                                          pcmk__ar_none);
 246         }
 247 
 248     } else {
 249         // Order this member relative to the previous one
 250 
 251         pcmk__order_starts(member_data->previous_member, member,
 252                            pcmk__ar_first_implies_then
 253                            |pcmk__ar_unrunnable_first_blocks);
 254         pcmk__order_stops(member, member_data->previous_member,
 255                           pcmk__ar_ordered|pcmk__ar_intermediate_stop);
 256 
 257         /* In unusual circumstances (such as adding a new member to the middle
 258          * of a group with unmanaged later members), this member may be active
 259          * while the previous (new) member is inactive. In this situation, the
 260          * usual restart orderings will be irrelevant, so we need to order this
 261          * member's stop before the previous member's start.
 262          */
 263         if ((member->running_on != NULL)
 264             && (member_data->previous_member->running_on == NULL)) {
 265             pcmk__order_resource_actions(member, PCMK_ACTION_STOP,
 266                                          member_data->previous_member,
 267                                          PCMK_ACTION_START,
 268                                          pcmk__ar_then_implies_first
 269                                          |pcmk__ar_unrunnable_first_blocks);
 270         }
 271 
 272         if (member_data->promotable) {
 273             pcmk__order_resource_actions(member_data->previous_member,
 274                                          PCMK_ACTION_PROMOTE, member,
 275                                          PCMK_ACTION_PROMOTE,
 276                                          pcmk__ar_first_implies_then
 277                                          |pcmk__ar_unrunnable_first_blocks);
 278             pcmk__order_resource_actions(member, PCMK_ACTION_DEMOTE,
 279                                          member_data->previous_member,
 280                                          PCMK_ACTION_DEMOTE, pcmk__ar_ordered);
 281         }
 282     }
 283 
 284     // Make sure partially active groups shut down in sequence
 285     if (member->running_on != NULL) {
 286         if (member_data->ordered && (member_data->previous_member != NULL)
 287             && (member_data->previous_member->running_on == NULL)
 288             && (member_data->last_active != NULL)
 289             && (member_data->last_active->running_on != NULL)) {
 290             pcmk__order_stops(member, member_data->last_active,
 291                               pcmk__ar_ordered);
 292         }
 293         member_data->last_active = member;
 294     }
 295 
 296     member_data->previous_member = member;
 297 }
 298 
 299 /*!
 300  * \internal
 301  * \brief Create implicit constraints needed for a group resource
 302  *
 303  * \param[in,out] rsc  Group resource to create implicit constraints for
 304  */
 305 void
 306 pcmk__group_internal_constraints(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 307 {
 308     struct member_data member_data = { false, };
 309     const pcmk_resource_t *top = NULL;
 310 
 311     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
 312 
 313     /* Order group pseudo-actions relative to each other for restarting:
 314      * stop group -> group is stopped -> start group -> group is started
 315      */
 316     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP,
 317                                  rsc, PCMK_ACTION_STOPPED,
 318                                  pcmk__ar_unrunnable_first_blocks);
 319     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
 320                                  rsc, PCMK_ACTION_START,
 321                                  pcmk__ar_ordered);
 322     pcmk__order_resource_actions(rsc, PCMK_ACTION_START,
 323                                  rsc, PCMK_ACTION_RUNNING,
 324                                  pcmk__ar_unrunnable_first_blocks);
 325 
 326     top = pe__const_top_resource(rsc, false);
 327 
 328     member_data.ordered = pe__group_flag_is_set(rsc, pcmk__group_ordered);
 329     member_data.colocated = pe__group_flag_is_set(rsc, pcmk__group_colocated);
 330     member_data.promotable = pcmk_is_set(top->flags, pcmk_rsc_promotable);
 331     g_list_foreach(rsc->children, member_internal_constraints, &member_data);
 332 }
 333 
 334 /*!
 335  * \internal
 336  * \brief Apply a colocation's score to node scores or resource priority
 337  *
 338  * Given a colocation constraint for a group with some other resource, apply the
 339  * score to the dependent's allowed node scores (if we are still placing
 340  * resources) or priority (if we are choosing promotable clone instance roles).
 341  *
 342  * \param[in,out] dependent      Dependent group resource in colocation
 343  * \param[in]     primary        Primary resource in colocation
 344  * \param[in]     colocation     Colocation constraint to apply
 345  */
 346 static void
 347 colocate_group_with(pcmk_resource_t *dependent, const pcmk_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
 348                     const pcmk__colocation_t *colocation)
 349 {
 350     pcmk_resource_t *member = NULL;
 351 
 352     if (dependent->children == NULL) {
 353         return;
 354     }
 355 
 356     pe_rsc_trace(primary, "Processing %s (group %s with %s) for dependent",
 357                  colocation->id, dependent->id, primary->id);
 358 
 359     if (pe__group_flag_is_set(dependent, pcmk__group_colocated)) {
 360         // Colocate first member (internal colocations will handle the rest)
 361         member = (pcmk_resource_t *) dependent->children->data;
 362         member->cmds->apply_coloc_score(member, primary, colocation, true);
 363         return;
 364     }
 365 
 366     if (colocation->score >= INFINITY) {
 367         pcmk__config_err("%s: Cannot perform mandatory colocation between "
 368                          "non-colocated group and %s",
 369                          dependent->id, primary->id);
 370         return;
 371     }
 372 
 373     // Colocate each member individually
 374     for (GList *iter = dependent->children; iter != NULL; iter = iter->next) {
 375         member = (pcmk_resource_t *) iter->data;
 376         member->cmds->apply_coloc_score(member, primary, colocation, true);
 377     }
 378 }
 379 
 380 /*!
 381  * \internal
 382  * \brief Apply a colocation's score to node scores or resource priority
 383  *
 384  * Given a colocation constraint for some other resource with a group, apply the
 385  * score to the dependent's allowed node scores (if we are still placing
 386  * resources) or priority (if we are choosing promotable clone instance roles).
 387  *
 388  * \param[in,out] dependent      Dependent resource in colocation
 389  * \param[in]     primary        Primary group resource in colocation
 390  * \param[in]     colocation     Colocation constraint to apply
 391  */
 392 static void
 393 colocate_with_group(pcmk_resource_t *dependent, const pcmk_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
 394                     const pcmk__colocation_t *colocation)
 395 {
 396     const pcmk_resource_t *member = NULL;
 397 
 398     pe_rsc_trace(primary,
 399                  "Processing colocation %s (%s with group %s) for primary",
 400                  colocation->id, dependent->id, primary->id);
 401 
 402     if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
 403         return;
 404     }
 405 
 406     if (pe__group_flag_is_set(primary, pcmk__group_colocated)) {
 407 
 408         if (colocation->score >= INFINITY) {
 409             /* For mandatory colocations, the entire group must be assignable
 410              * (and in the specified role if any), so apply the colocation based
 411              * on the last member.
 412              */
 413             member = pe__last_group_member(primary);
 414         } else if (primary->children != NULL) {
 415             /* For optional colocations, whether the group is partially or fully
 416              * up doesn't matter, so apply the colocation based on the first
 417              * member.
 418              */
 419             member = (pcmk_resource_t *) primary->children->data;
 420         }
 421         if (member == NULL) {
 422             return; // Nothing to colocate with
 423         }
 424 
 425         member->cmds->apply_coloc_score(dependent, member, colocation, false);
 426         return;
 427     }
 428 
 429     if (colocation->score >= INFINITY) {
 430         pcmk__config_err("%s: Cannot perform mandatory colocation with"
 431                          " non-colocated group %s",
 432                          dependent->id, primary->id);
 433         return;
 434     }
 435 
 436     // Colocate dependent with each member individually
 437     for (const GList *iter = primary->children; iter != NULL;
 438          iter = iter->next) {
 439         member = iter->data;
 440         member->cmds->apply_coloc_score(dependent, member, colocation, false);
 441     }
 442 }
 443 
 444 /*!
 445  * \internal
 446  * \brief Apply a colocation's score to node scores or resource priority
 447  *
 448  * Given a colocation constraint, apply its score to the dependent's
 449  * allowed node scores (if we are still placing resources) or priority (if
 450  * we are choosing promotable clone instance roles).
 451  *
 452  * \param[in,out] dependent      Dependent resource in colocation
 453  * \param[in]     primary        Primary resource in colocation
 454  * \param[in]     colocation     Colocation constraint to apply
 455  * \param[in]     for_dependent  true if called on behalf of dependent
 456  */
 457 void
 458 pcmk__group_apply_coloc_score(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
 459                               const pcmk_resource_t *primary,
 460                               const pcmk__colocation_t *colocation,
 461                               bool for_dependent)
 462 {
 463     CRM_ASSERT((dependent != NULL) && (primary != NULL)
 464                && (colocation != NULL));
 465 
 466     if (for_dependent) {
 467         colocate_group_with(dependent, primary, colocation);
 468 
 469     } else {
 470         // Method should only be called for primitive dependents
 471         CRM_ASSERT(dependent->variant == pcmk_rsc_variant_primitive);
 472 
 473         colocate_with_group(dependent, primary, colocation);
 474     }
 475 }
 476 
 477 /*!
 478  * \internal
 479  * \brief Return action flags for a given group resource action
 480  *
 481  * \param[in,out] action  Group action to get flags for
 482  * \param[in]     node    If not NULL, limit effects to this node
 483  *
 484  * \return Flags appropriate to \p action on \p node
 485  */
 486 uint32_t
 487 pcmk__group_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 488 {
 489     // Default flags for a group action
 490     uint32_t flags = pcmk_action_optional
 491                      |pcmk_action_runnable
 492                      |pcmk_action_pseudo;
 493 
 494     CRM_ASSERT(action != NULL);
 495 
 496     // Update flags considering each member's own flags for same action
 497     for (GList *iter = action->rsc->children; iter != NULL; iter = iter->next) {
 498         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
 499 
 500         // Check whether member has the same action
 501         enum action_tasks task = get_complex_task(member, action->task);
 502         const char *task_s = task2text(task);
 503         pcmk_action_t *member_action = find_first_action(member->actions, NULL,
 504                                                          task_s, node);
 505 
 506         if (member_action != NULL) {
 507             uint32_t member_flags = member->cmds->action_flags(member_action,
 508                                                                node);
 509 
 510             // Group action is mandatory if any member action is
 511             if (pcmk_is_set(flags, pcmk_action_optional)
 512                 && !pcmk_is_set(member_flags, pcmk_action_optional)) {
 513                 pe_rsc_trace(action->rsc, "%s is mandatory because %s is",
 514                              action->uuid, member_action->uuid);
 515                 pe__clear_raw_action_flags(flags, "group action",
 516                                            pcmk_action_optional);
 517                 pe__clear_action_flags(action, pcmk_action_optional);
 518             }
 519 
 520             // Group action is unrunnable if any member action is
 521             if (!pcmk__str_eq(task_s, action->task, pcmk__str_none)
 522                 && pcmk_is_set(flags, pcmk_action_runnable)
 523                 && !pcmk_is_set(member_flags, pcmk_action_runnable)) {
 524 
 525                 pe_rsc_trace(action->rsc, "%s is unrunnable because %s is",
 526                              action->uuid, member_action->uuid);
 527                 pe__clear_raw_action_flags(flags, "group action",
 528                                            pcmk_action_runnable);
 529                 pe__clear_action_flags(action, pcmk_action_runnable);
 530             }
 531 
 532         /* Group (pseudo-)actions other than stop or demote are unrunnable
 533          * unless every member will do it.
 534          */
 535         } else if ((task != pcmk_action_stop) && (task != pcmk_action_demote)) {
 536             pe_rsc_trace(action->rsc,
 537                          "%s is not runnable because %s will not %s",
 538                          action->uuid, member->id, task_s);
 539             pe__clear_raw_action_flags(flags, "group action",
 540                                        pcmk_action_runnable);
 541         }
 542     }
 543 
 544     return flags;
 545 }
 546 
 547 /*!
 548  * \internal
 549  * \brief Update two actions according to an ordering between them
 550  *
 551  * Given information about an ordering of two actions, update the actions' flags
 552  * (and runnable_before members if appropriate) as appropriate for the ordering.
 553  * Effects may cascade to other orderings involving the actions as well.
 554  *
 555  * \param[in,out] first      'First' action in an ordering
 556  * \param[in,out] then       'Then' action in an ordering
 557  * \param[in]     node       If not NULL, limit scope of ordering to this node
 558  *                           (only used when interleaving instances)
 559  * \param[in]     flags      Action flags for \p first for ordering purposes
 560  * \param[in]     filter     Action flags to limit scope of certain updates (may
 561  *                           include pcmk_action_optional to affect only
 562  *                           mandatory actions, and pcmk_action_runnable to
 563  *                           affect only runnable actions)
 564  * \param[in]     type       Group of enum pcmk__action_relation_flags to apply
 565  * \param[in,out] scheduler  Scheduler data
 566  *
 567  * \return Group of enum pcmk__updated flags indicating what was updated
 568  */
 569 uint32_t
 570 pcmk__group_update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then,
     /* [previous][next][first][last][top][bottom][index][help] */
 571                                    const pcmk_node_t *node, uint32_t flags,
 572                                    uint32_t filter, uint32_t type,
 573                                    pcmk_scheduler_t *scheduler)
 574 {
 575     uint32_t changed = pcmk__updated_none;
 576 
 577     // Group method can be called only on behalf of "then" action
 578     CRM_ASSERT((first != NULL) && (then != NULL) && (then->rsc != NULL)
 579                && (scheduler != NULL));
 580 
 581     // Update the actions for the group itself
 582     changed |= pcmk__update_ordered_actions(first, then, node, flags, filter,
 583                                             type, scheduler);
 584 
 585     // Update the actions for each group member
 586     for (GList *iter = then->rsc->children; iter != NULL; iter = iter->next) {
 587         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
 588 
 589         pcmk_action_t *member_action = find_first_action(member->actions, NULL,
 590                                                          then->task, node);
 591 
 592         if (member_action != NULL) {
 593             changed |= member->cmds->update_ordered_actions(first,
 594                                                             member_action, node,
 595                                                             flags, filter, type,
 596                                                             scheduler);
 597         }
 598     }
 599     return changed;
 600 }
 601 
 602 /*!
 603  * \internal
 604  * \brief Apply a location constraint to a group's allowed node scores
 605  *
 606  * \param[in,out] rsc       Group resource to apply constraint to
 607  * \param[in,out] location  Location constraint to apply
 608  */
 609 void
 610 pcmk__group_apply_location(pcmk_resource_t *rsc, pe__location_t *location)
     /* [previous][next][first][last][top][bottom][index][help] */
 611 {
 612     GList *node_list_orig = NULL;
 613     GList *node_list_copy = NULL;
 614     bool reset_scores = true;
 615 
 616     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
 617                && (location != NULL));
 618 
 619     node_list_orig = location->node_list_rh;
 620     node_list_copy = pcmk__copy_node_list(node_list_orig, true);
 621     reset_scores = pe__group_flag_is_set(rsc, pcmk__group_colocated);
 622 
 623     // Apply the constraint for the group itself (updates node scores)
 624     pcmk__apply_location(rsc, location);
 625 
 626     // Apply the constraint for each member
 627     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 628         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
 629 
 630         member->cmds->apply_location(member, location);
 631 
 632         if (reset_scores) {
 633             /* The first member of colocated groups needs to use the original
 634              * node scores, but subsequent members should work on a copy, since
 635              * the first member's scores already incorporate theirs.
 636              */
 637             reset_scores = false;
 638             location->node_list_rh = node_list_copy;
 639         }
 640     }
 641 
 642     location->node_list_rh = node_list_orig;
 643     g_list_free_full(node_list_copy, free);
 644 }
 645 
 646 // Group implementation of pcmk_assignment_methods_t:colocated_resources()
 647 GList *
 648 pcmk__group_colocated_resources(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 649                                 const pcmk_resource_t *orig_rsc,
 650                                 GList *colocated_rscs)
 651 {
 652     const pcmk_resource_t *member = NULL;
 653 
 654     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
 655 
 656     if (orig_rsc == NULL) {
 657         orig_rsc = rsc;
 658     }
 659 
 660     if (pe__group_flag_is_set(rsc, pcmk__group_colocated)
 661         || pe_rsc_is_clone(rsc->parent)) {
 662         /* This group has colocated members and/or is cloned -- either way,
 663          * add every child's colocated resources to the list. The first and last
 664          * members will include the group's own colocations.
 665          */
 666         colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
 667         for (const GList *iter = rsc->children;
 668              iter != NULL; iter = iter->next) {
 669 
 670             member = (const pcmk_resource_t *) iter->data;
 671             colocated_rscs = member->cmds->colocated_resources(member, orig_rsc,
 672                                                                colocated_rscs);
 673         }
 674 
 675     } else if (rsc->children != NULL) {
 676         /* This group's members are not colocated, and the group is not cloned,
 677          * so just add the group's own colocations to the list.
 678          */
 679         colocated_rscs = pcmk__colocated_resources(rsc, orig_rsc,
 680                                                    colocated_rscs);
 681     }
 682 
 683     return colocated_rscs;
 684 }
 685 
 686 // Group implementation of pcmk_assignment_methods_t:with_this_colocations()
 687 void
 688 pcmk__with_group_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 689                              const pcmk_resource_t *orig_rsc, GList **list)
 690 
 691 {
 692     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
 693                && (orig_rsc != NULL) && (list != NULL));
 694 
 695     // Ignore empty groups
 696     if (rsc->children == NULL) {
 697         return;
 698     }
 699 
 700     /* "With this" colocations are needed only for the group itself and for its
 701      * last member. (Previous members will chain via the group internal
 702      * colocations.)
 703      */
 704     if ((orig_rsc != rsc) && (orig_rsc != pe__last_group_member(rsc))) {
 705         return;
 706     }
 707 
 708     pe_rsc_trace(rsc, "Adding 'with %s' colocations to list for %s",
 709                  rsc->id, orig_rsc->id);
 710 
 711     // Add the group's own colocations
 712     pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
 713 
 714     // If cloned, add any relevant colocations with the clone
 715     if (rsc->parent != NULL) {
 716         rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc,
 717                                                  list);
 718     }
 719 
 720     if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) {
 721         // @COMPAT Non-colocated groups are deprecated
 722         return;
 723     }
 724 
 725     // Add explicit colocations with the group's (other) children
 726     for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 727         const pcmk_resource_t *member = iter->data;
 728 
 729         if (member != orig_rsc) {
 730             member->cmds->with_this_colocations(member, orig_rsc, list);
 731         }
 732     }
 733 }
 734 
 735 // Group implementation of pcmk_assignment_methods_t:this_with_colocations()
 736 void
 737 pcmk__group_with_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 738                              const pcmk_resource_t *orig_rsc, GList **list)
 739 {
 740     const pcmk_resource_t *member = NULL;
 741 
 742     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
 743                && (orig_rsc != NULL) && (list != NULL));
 744 
 745     // Ignore empty groups
 746     if (rsc->children == NULL) {
 747         return;
 748     }
 749 
 750     /* "This with" colocations are normally needed only for the group itself and
 751      * for its first member.
 752      */
 753     if ((rsc == orig_rsc)
 754         || (orig_rsc == (const pcmk_resource_t *) rsc->children->data)) {
 755         pe_rsc_trace(rsc, "Adding '%s with' colocations to list for %s",
 756                      rsc->id, orig_rsc->id);
 757 
 758         // Add the group's own colocations
 759         pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
 760 
 761         // If cloned, add any relevant colocations involving the clone
 762         if (rsc->parent != NULL) {
 763             rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc,
 764                                                      list);
 765         }
 766 
 767         if (!pe__group_flag_is_set(rsc, pcmk__group_colocated)) {
 768             // @COMPAT Non-colocated groups are deprecated
 769             return;
 770         }
 771 
 772         // Add explicit colocations involving the group's (other) children
 773         for (const GList *iter = rsc->children;
 774              iter != NULL; iter = iter->next) {
 775             member = iter->data;
 776             if (member != orig_rsc) {
 777                 member->cmds->this_with_colocations(member, orig_rsc, list);
 778             }
 779         }
 780         return;
 781     }
 782 
 783     /* Later group members honor the group's colocations indirectly, due to the
 784      * internal group colocations that chain everything from the first member.
 785      * However, if an earlier group member is unmanaged, this chaining will not
 786      * happen, so the group's mandatory colocations must be explicitly added.
 787      */
 788     for (const GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 789         member = iter->data;
 790         if (orig_rsc == member) {
 791             break; // We've seen all earlier members, and none are unmanaged
 792         }
 793 
 794         if (!pcmk_is_set(member->flags, pcmk_rsc_managed)) {
 795             crm_trace("Adding mandatory '%s with' colocations to list for "
 796                       "member %s because earlier member %s is unmanaged",
 797                       rsc->id, orig_rsc->id, member->id);
 798             for (const GList *cons_iter = rsc->rsc_cons; cons_iter != NULL;
 799                  cons_iter = cons_iter->next) {
 800                 const pcmk__colocation_t *colocation = NULL;
 801 
 802                 colocation = (const pcmk__colocation_t *) cons_iter->data;
 803                 if (colocation->score == INFINITY) {
 804                     pcmk__add_this_with(list, colocation, orig_rsc);
 805                 }
 806             }
 807             // @TODO Add mandatory (or all?) clone constraints if cloned
 808             break;
 809         }
 810     }
 811 }
 812 
 813 /*!
 814  * \internal
 815  * \brief Update nodes with scores of colocated resources' nodes
 816  *
 817  * Given a table of nodes and a resource, update the nodes' scores with the
 818  * scores of the best nodes matching the attribute used for each of the
 819  * resource's relevant colocations.
 820  *
 821  * \param[in,out] source_rsc  Group resource whose node scores to add
 822  * \param[in]     target_rsc  Resource on whose behalf to update \p *nodes
 823  * \param[in]     log_id      Resource ID for logs (if \c NULL, use
 824  *                            \p source_rsc ID)
 825  * \param[in,out] nodes       Nodes to update (set initial contents to \c NULL
 826  *                            to copy allowed nodes from \p source_rsc)
 827  * \param[in]     colocation  Original colocation constraint (used to get
 828  *                            configured primary resource's stickiness, and
 829  *                            to get colocation node attribute; if \c NULL,
 830  *                            <tt>source_rsc</tt>'s own matching node scores will
 831  *                            not be added, and \p *nodes must be \c NULL as
 832  *                            well)
 833  * \param[in]     factor      Incorporate scores multiplied by this factor
 834  * \param[in]     flags       Bitmask of enum pcmk__coloc_select values
 835  *
 836  * \note \c NULL \p target_rsc, \c NULL \p *nodes, \c NULL \p colocation, and
 837  *       the \c pcmk__coloc_select_this_with flag are used together (and only by
 838  *       \c cmp_resources()).
 839  * \note The caller remains responsible for freeing \p *nodes.
 840  * \note This is the group implementation of
 841  *       \c pcmk_assignment_methods_t:add_colocated_node_scores().
 842  */
 843 void
 844 pcmk__group_add_colocated_node_scores(pcmk_resource_t *source_rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 845                                       const pcmk_resource_t *target_rsc,
 846                                       const char *log_id, GHashTable **nodes,
 847                                       const pcmk__colocation_t *colocation,
 848                                       float factor, uint32_t flags)
 849 {
 850     pcmk_resource_t *member = NULL;
 851 
 852     CRM_ASSERT((source_rsc != NULL)
 853                && (source_rsc->variant == pcmk_rsc_variant_group)
 854                && (nodes != NULL)
 855                && ((colocation != NULL)
 856                    || ((target_rsc == NULL) && (*nodes == NULL))));
 857 
 858     if (log_id == NULL) {
 859         log_id = source_rsc->id;
 860     }
 861 
 862     // Avoid infinite recursion
 863     if (pcmk_is_set(source_rsc->flags, pcmk_rsc_updating_nodes)) {
 864         pe_rsc_info(source_rsc, "%s: Breaking dependency loop at %s",
 865                     log_id, source_rsc->id);
 866         return;
 867     }
 868     pe__set_resource_flags(source_rsc, pcmk_rsc_updating_nodes);
 869 
 870     // Ignore empty groups (only possible with schema validation disabled)
 871     if (source_rsc->children == NULL) {
 872         return;
 873     }
 874 
 875     /* Refer the operation to the first or last member as appropriate.
 876      *
 877      * cmp_resources() is the only caller that passes a NULL nodes table,
 878      * and is also the only caller using pcmk__coloc_select_this_with.
 879      * For "this with" colocations, the last member will recursively incorporate
 880      * all the other members' "this with" colocations via the internal group
 881      * colocations (and via the first member, the group's own colocations).
 882      *
 883      * For "with this" colocations, the first member works similarly.
 884      */
 885     if (*nodes == NULL) {
 886         member = pe__last_group_member(source_rsc);
 887     } else {
 888         member = source_rsc->children->data;
 889     }
 890     pe_rsc_trace(source_rsc, "%s: Merging scores from group %s using member %s "
 891                  "(at %.6f)", log_id, source_rsc->id, member->id, factor);
 892     member->cmds->add_colocated_node_scores(member, target_rsc, log_id, nodes,
 893                                             colocation, factor, flags);
 894     pe__clear_resource_flags(source_rsc, pcmk_rsc_updating_nodes);
 895 }
 896 
 897 // Group implementation of pcmk_assignment_methods_t:add_utilization()
 898 void
 899 pcmk__group_add_utilization(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 900                             const pcmk_resource_t *orig_rsc, GList *all_rscs,
 901                             GHashTable *utilization)
 902 {
 903     pcmk_resource_t *member = NULL;
 904 
 905     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group)
 906                && (orig_rsc != NULL) && (utilization != NULL));
 907 
 908     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
 909         return;
 910     }
 911 
 912     pe_rsc_trace(orig_rsc, "%s: Adding group %s as colocated utilization",
 913                  orig_rsc->id, rsc->id);
 914     if (pe__group_flag_is_set(rsc, pcmk__group_colocated)
 915         || pe_rsc_is_clone(rsc->parent)) {
 916         // Every group member will be on same node, so sum all members
 917         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 918             member = (pcmk_resource_t *) iter->data;
 919 
 920             if (pcmk_is_set(member->flags, pcmk_rsc_unassigned)
 921                 && (g_list_find(all_rscs, member) == NULL)) {
 922                 member->cmds->add_utilization(member, orig_rsc, all_rscs,
 923                                               utilization);
 924             }
 925         }
 926 
 927     } else if (rsc->children != NULL) {
 928         // Just add first member's utilization
 929         member = (pcmk_resource_t *) rsc->children->data;
 930         if ((member != NULL)
 931             && pcmk_is_set(member->flags, pcmk_rsc_unassigned)
 932             && (g_list_find(all_rscs, member) == NULL)) {
 933 
 934             member->cmds->add_utilization(member, orig_rsc, all_rscs,
 935                                           utilization);
 936         }
 937     }
 938 }
 939 
 940 void
 941 pcmk__group_shutdown_lock(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 942 {
 943     CRM_ASSERT((rsc != NULL) && (rsc->variant == pcmk_rsc_variant_group));
 944 
 945     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 946         pcmk_resource_t *member = (pcmk_resource_t *) iter->data;
 947 
 948         member->cmds->shutdown_lock(member);
 949     }
 950 }

/* [previous][next][first][last][top][bottom][index][help] */