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

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