root/lib/pacemaker/pcmk_sched_bundle.c

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

DEFINITIONS

This source file includes following definitions.
  1. assign_replica
  2. pcmk__bundle_assign
  3. create_replica_actions
  4. pcmk__bundle_create_actions
  5. replica_internal_constraints
  6. pcmk__bundle_internal_constraints
  7. match_replica_container
  8. get_bundle_node_host
  9. compatible_container
  10. replica_apply_coloc_score
  11. pcmk__bundle_apply_coloc_score
  12. pcmk__with_bundle_colocations
  13. pcmk__bundle_with_colocations
  14. pcmk__bundle_action_flags
  15. apply_location_to_replica
  16. pcmk__bundle_apply_location
  17. add_replica_actions_to_graph
  18. pcmk__bundle_add_actions_to_graph
  19. order_replica_start_after
  20. create_replica_probes
  21. pcmk__bundle_create_probe
  22. output_replica_actions
  23. pcmk__output_bundle_actions
  24. pcmk__bundle_add_utilization
  25. pcmk__bundle_shutdown_lock

   1 /*
   2  * Copyright 2004-2025 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 <libxml/tree.h>            // xmlNode
  15 
  16 #include <crm/common/xml.h>
  17 #include <pacemaker-internal.h>
  18 
  19 #include "libpacemaker_private.h"
  20 
  21 struct assign_data {
  22     const pcmk_node_t *prefer;
  23     bool stop_if_fail;
  24 };
  25 
  26 /*!
  27  * \internal
  28  * \brief Assign a single bundle replica's resources (other than container)
  29  *
  30  * \param[in,out] replica    Replica to assign
  31  * \param[in]     user_data  Preferred node, if any
  32  *
  33  * \return true (to indicate that any further replicas should be processed)
  34  */
  35 static bool
  36 assign_replica(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
  37 {
  38     pcmk_node_t *container_host = NULL;
  39 
  40     struct assign_data *assign_data = user_data;
  41     const pcmk_node_t *prefer = assign_data->prefer;
  42     bool stop_if_fail = assign_data->stop_if_fail;
  43 
  44     const pcmk_resource_t *bundle = pe__const_top_resource(replica->container,
  45                                                            true);
  46 
  47     if (replica->ip != NULL) {
  48         pcmk__rsc_trace(bundle, "Assigning bundle %s IP %s",
  49                         bundle->id, replica->ip->id);
  50         replica->ip->priv->cmds->assign(replica->ip, prefer, stop_if_fail);
  51     }
  52 
  53     container_host = replica->container->priv->assigned_node;
  54     if (replica->remote != NULL) {
  55         if (pcmk__is_pacemaker_remote_node(container_host)) {
  56             /* REMOTE_CONTAINER_HACK: "Nested" connection resources must be on
  57              * the same host because Pacemaker Remote only supports a single
  58              * active connection.
  59              */
  60             pcmk__new_colocation("#replica-remote-with-host-remote", NULL,
  61                                  PCMK_SCORE_INFINITY, replica->remote,
  62                                  container_host->priv->remote, NULL,
  63                                  NULL, pcmk__coloc_influence);
  64         }
  65         pcmk__rsc_trace(bundle, "Assigning bundle %s connection %s",
  66                         bundle->id, replica->remote->id);
  67         replica->remote->priv->cmds->assign(replica->remote, prefer,
  68                                             stop_if_fail);
  69     }
  70 
  71     if (replica->child != NULL) {
  72         pcmk_node_t *node = NULL;
  73         GHashTableIter iter;
  74 
  75         g_hash_table_iter_init(&iter, replica->child->priv->allowed_nodes);
  76         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &node)) {
  77             if (!pcmk__same_node(node, replica->node)) {
  78                 node->assign->score = -PCMK_SCORE_INFINITY;
  79             } else if (!pcmk__threshold_reached(replica->child, node, NULL)) {
  80                 node->assign->score = PCMK_SCORE_INFINITY;
  81             }
  82         }
  83 
  84         pcmk__set_rsc_flags(replica->child->priv->parent,
  85                             pcmk__rsc_assigning);
  86         pcmk__rsc_trace(bundle, "Assigning bundle %s replica child %s",
  87                         bundle->id, replica->child->id);
  88         replica->child->priv->cmds->assign(replica->child, replica->node,
  89                                            stop_if_fail);
  90         pcmk__clear_rsc_flags(replica->child->priv->parent,
  91                               pcmk__rsc_assigning);
  92     }
  93     return true;
  94 }
  95 
  96 /*!
  97  * \internal
  98  * \brief Assign a bundle resource to a node
  99  *
 100  * \param[in,out] rsc           Resource to assign to a node
 101  * \param[in]     prefer        Node to prefer, if all else is equal
 102  * \param[in]     stop_if_fail  If \c true and a primitive descendant of \p rsc
 103  *                              can't be assigned to a node, set the
 104  *                              descendant's next role to stopped and update
 105  *                              existing actions
 106  *
 107  * \return Node that \p rsc is assigned to, if assigned entirely to one node
 108  *
 109  * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
 110  *       completely undo the assignment. A successful assignment can be either
 111  *       undone or left alone as final. A failed assignment has the same effect
 112  *       as calling pcmk__unassign_resource(); there are no side effects on
 113  *       roles or actions.
 114  */
 115 pcmk_node_t *
 116 pcmk__bundle_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
     /* [previous][next][first][last][top][bottom][index][help] */
 117                     bool stop_if_fail)
 118 {
 119     GList *containers = NULL;
 120     pcmk_resource_t *bundled_resource = NULL;
 121     struct assign_data assign_data = { prefer, stop_if_fail };
 122 
 123     pcmk__assert(pcmk__is_bundle(rsc));
 124 
 125     pcmk__rsc_trace(rsc, "Assigning bundle %s", rsc->id);
 126     pcmk__set_rsc_flags(rsc, pcmk__rsc_assigning);
 127 
 128     pe__show_node_scores(!pcmk_is_set(rsc->priv->scheduler->flags,
 129                                       pcmk__sched_output_scores),
 130                          rsc, __func__, rsc->priv->allowed_nodes,
 131                          rsc->priv->scheduler);
 132 
 133     // Assign all containers first, so we know what nodes the bundle will be on
 134     containers = g_list_sort(pe__bundle_containers(rsc), pcmk__cmp_instance);
 135     pcmk__assign_instances(rsc, containers, pe__bundle_max(rsc),
 136                            rsc->priv->fns->max_per_node(rsc));
 137     g_list_free(containers);
 138 
 139     // Then assign remaining replica resources
 140     pe__foreach_bundle_replica(rsc, assign_replica, (void *) &assign_data);
 141 
 142     // Finally, assign the bundled resources to each bundle node
 143     bundled_resource = pe__bundled_resource(rsc);
 144     if (bundled_resource != NULL) {
 145         pcmk_node_t *node = NULL;
 146         GHashTableIter iter;
 147 
 148         g_hash_table_iter_init(&iter, bundled_resource->priv->allowed_nodes);
 149         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) & node)) {
 150             if (pe__node_is_bundle_instance(rsc, node)) {
 151                 node->assign->score = 0;
 152             } else {
 153                 node->assign->score = -PCMK_SCORE_INFINITY;
 154             }
 155         }
 156         bundled_resource->priv->cmds->assign(bundled_resource, prefer,
 157                                                 stop_if_fail);
 158     }
 159 
 160     pcmk__clear_rsc_flags(rsc, pcmk__rsc_assigning|pcmk__rsc_unassigned);
 161     return NULL;
 162 }
 163 
 164 /*!
 165  * \internal
 166  * \brief Create actions for a bundle replica's resources (other than child)
 167  *
 168  * \param[in,out] replica    Replica to create actions for
 169  * \param[in]     user_data  Unused
 170  *
 171  * \return true (to indicate that any further replicas should be processed)
 172  */
 173 static bool
 174 create_replica_actions(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 175 {
 176     if (replica->ip != NULL) {
 177         replica->ip->priv->cmds->create_actions(replica->ip);
 178     }
 179     if (replica->container != NULL) {
 180         replica->container->priv->cmds->create_actions(replica->container);
 181     }
 182     if (replica->remote != NULL) {
 183         replica->remote->priv->cmds->create_actions(replica->remote);
 184     }
 185     return true;
 186 }
 187 
 188 /*!
 189  * \internal
 190  * \brief Create all actions needed for a given bundle resource
 191  *
 192  * \param[in,out] rsc  Bundle resource to create actions for
 193  */
 194 void
 195 pcmk__bundle_create_actions(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 196 {
 197     pcmk_action_t *action = NULL;
 198     GList *containers = NULL;
 199     pcmk_resource_t *bundled_resource = NULL;
 200 
 201     pcmk__assert(pcmk__is_bundle(rsc));
 202 
 203     pe__foreach_bundle_replica(rsc, create_replica_actions, NULL);
 204 
 205     containers = pe__bundle_containers(rsc);
 206     pcmk__create_instance_actions(rsc, containers);
 207     g_list_free(containers);
 208 
 209     bundled_resource = pe__bundled_resource(rsc);
 210     if (bundled_resource != NULL) {
 211         bundled_resource->priv->cmds->create_actions(bundled_resource);
 212 
 213         if (pcmk_is_set(bundled_resource->flags, pcmk__rsc_promotable)) {
 214             pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTE, true, true);
 215             action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_PROMOTED,
 216                                                true, true);
 217             action->priority = PCMK_SCORE_INFINITY;
 218 
 219             pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTE, true, true);
 220             action = pe__new_rsc_pseudo_action(rsc, PCMK_ACTION_DEMOTED,
 221                                                true, true);
 222             action->priority = PCMK_SCORE_INFINITY;
 223         }
 224     }
 225 }
 226 
 227 /*!
 228  * \internal
 229  * \brief Create internal constraints for a bundle replica's resources
 230  *
 231  * \param[in,out] replica    Replica to create internal constraints for
 232  * \param[in,out] user_data  Replica's parent bundle
 233  *
 234  * \return true (to indicate that any further replicas should be processed)
 235  */
 236 static bool
 237 replica_internal_constraints(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 238 {
 239     pcmk_resource_t *bundle = user_data;
 240 
 241     replica->container->priv->cmds->internal_constraints(replica->container);
 242 
 243     // Start bundle -> start replica container
 244     pcmk__order_starts(bundle, replica->container,
 245                        pcmk__ar_unrunnable_first_blocks
 246                        |pcmk__ar_then_implies_first_graphed);
 247 
 248     // Stop bundle -> stop replica child and container
 249     if (replica->child != NULL) {
 250         pcmk__order_stops(bundle, replica->child,
 251                           pcmk__ar_then_implies_first_graphed);
 252     }
 253     pcmk__order_stops(bundle, replica->container,
 254                       pcmk__ar_then_implies_first_graphed);
 255 
 256     // Start replica container -> bundle is started
 257     pcmk__order_resource_actions(replica->container, PCMK_ACTION_START, bundle,
 258                                  PCMK_ACTION_RUNNING,
 259                                  pcmk__ar_first_implies_then_graphed);
 260 
 261     // Stop replica container -> bundle is stopped
 262     pcmk__order_resource_actions(replica->container, PCMK_ACTION_STOP, bundle,
 263                                  PCMK_ACTION_STOPPED,
 264                                  pcmk__ar_first_implies_then_graphed);
 265 
 266     if (replica->ip != NULL) {
 267         replica->ip->priv->cmds->internal_constraints(replica->ip);
 268 
 269         // Replica IP address -> replica container (symmetric)
 270         pcmk__order_starts(replica->ip, replica->container,
 271                            pcmk__ar_unrunnable_first_blocks
 272                            |pcmk__ar_guest_allowed);
 273         pcmk__order_stops(replica->container, replica->ip,
 274                           pcmk__ar_then_implies_first|pcmk__ar_guest_allowed);
 275 
 276         pcmk__new_colocation("#ip-with-container", NULL, PCMK_SCORE_INFINITY,
 277                              replica->ip, replica->container, NULL, NULL,
 278                              pcmk__coloc_influence);
 279     }
 280 
 281     if (replica->remote != NULL) {
 282         /* This handles ordering and colocating remote relative to container
 283          * (via "#resource-with-container"). Since IP is also ordered and
 284          * colocated relative to the container, we don't need to do anything
 285          * explicit here with IP.
 286          */
 287         replica->remote->priv->cmds->internal_constraints(replica->remote);
 288     }
 289 
 290     if (replica->child != NULL) {
 291         pcmk__assert(replica->remote != NULL);
 292         // "Start remote then child" is implicit in scheduler's remote logic
 293     }
 294     return true;
 295 }
 296 
 297 /*!
 298  * \internal
 299  * \brief Create implicit constraints needed for a bundle resource
 300  *
 301  * \param[in,out] rsc  Bundle resource to create implicit constraints for
 302  */
 303 void
 304 pcmk__bundle_internal_constraints(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 305 {
 306     pcmk_resource_t *bundled_resource = NULL;
 307 
 308     pcmk__assert(pcmk__is_bundle(rsc));
 309 
 310     pe__foreach_bundle_replica(rsc, replica_internal_constraints, rsc);
 311 
 312     bundled_resource = pe__bundled_resource(rsc);
 313     if (bundled_resource == NULL) {
 314         return;
 315     }
 316 
 317     // Start bundle -> start bundled clone
 318     pcmk__order_resource_actions(rsc, PCMK_ACTION_START, bundled_resource,
 319                                  PCMK_ACTION_START,
 320                                  pcmk__ar_then_implies_first_graphed);
 321 
 322     // Bundled clone is started -> bundle is started
 323     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_RUNNING,
 324                                  rsc, PCMK_ACTION_RUNNING,
 325                                  pcmk__ar_first_implies_then_graphed);
 326 
 327     // Stop bundle -> stop bundled clone
 328     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP, bundled_resource,
 329                                  PCMK_ACTION_STOP,
 330                                  pcmk__ar_then_implies_first_graphed);
 331 
 332     // Bundled clone is stopped -> bundle is stopped
 333     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_STOPPED,
 334                                  rsc, PCMK_ACTION_STOPPED,
 335                                  pcmk__ar_first_implies_then_graphed);
 336 
 337     bundled_resource->priv->cmds->internal_constraints(bundled_resource);
 338 
 339     if (!pcmk_is_set(bundled_resource->flags, pcmk__rsc_promotable)) {
 340         return;
 341     }
 342     pcmk__promotable_restart_ordering(rsc);
 343 
 344     // Demote bundle -> demote bundled clone
 345     pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTE, bundled_resource,
 346                                  PCMK_ACTION_DEMOTE,
 347                                  pcmk__ar_then_implies_first_graphed);
 348 
 349     // Bundled clone is demoted -> bundle is demoted
 350     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_DEMOTED,
 351                                  rsc, PCMK_ACTION_DEMOTED,
 352                                  pcmk__ar_first_implies_then_graphed);
 353 
 354     // Promote bundle -> promote bundled clone
 355     pcmk__order_resource_actions(rsc, PCMK_ACTION_PROMOTE,
 356                                  bundled_resource, PCMK_ACTION_PROMOTE,
 357                                  pcmk__ar_then_implies_first_graphed);
 358 
 359     // Bundled clone is promoted -> bundle is promoted
 360     pcmk__order_resource_actions(bundled_resource, PCMK_ACTION_PROMOTED,
 361                                  rsc, PCMK_ACTION_PROMOTED,
 362                                  pcmk__ar_first_implies_then_graphed);
 363 }
 364 
 365 struct match_data {
 366     const pcmk_node_t *node;    // Node to compare against replica
 367     pcmk_resource_t *container; // Replica container corresponding to node
 368 };
 369 
 370 /*!
 371  * \internal
 372  * \brief Check whether a replica container is assigned to a given node
 373  *
 374  * \param[in]     replica    Replica to check
 375  * \param[in,out] user_data  struct match_data with node to compare against
 376  *
 377  * \return true if the replica does not match (to indicate further replicas
 378  *         should be processed), otherwise false
 379  */
 380 static bool
 381 match_replica_container(const pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 382 {
 383     struct match_data *match_data = user_data;
 384 
 385     if (pcmk__instance_matches(replica->container, match_data->node,
 386                                pcmk_role_unknown, false)) {
 387         match_data->container = replica->container;
 388         return false; // Match found, don't bother searching further replicas
 389     }
 390     return true; // No match, keep searching
 391 }
 392 
 393 /*!
 394  * \internal
 395  * \brief Get the host to which a bundle node is assigned
 396  *
 397  * \param[in] node  Possible bundle node to check
 398  *
 399  * \return Node to which the container for \p node is assigned if \p node is a
 400  *         bundle node, otherwise \p node itself
 401  */
 402 static const pcmk_node_t *
 403 get_bundle_node_host(const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 404 {
 405     if (pcmk__is_bundle_node(node)) {
 406         const pcmk_resource_t *container = NULL;
 407 
 408         container = node->priv->remote->priv->launcher;
 409         return container->priv->fns->location(container, NULL,
 410                                               pcmk__rsc_node_assigned);
 411     }
 412     return node;
 413 }
 414 
 415 /*!
 416  * \internal
 417  * \brief Find a bundle container compatible with a dependent resource
 418  *
 419  * \param[in] dependent  Dependent resource in colocation with bundle
 420  * \param[in] bundle     Bundle that \p dependent is colocated with
 421  *
 422  * \return A container from \p bundle assigned to the same node as \p dependent
 423  *         if assigned, otherwise assigned to any of dependent's allowed nodes,
 424  *         otherwise NULL.
 425  */
 426 static pcmk_resource_t *
 427 compatible_container(const pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
 428                      const pcmk_resource_t *bundle)
 429 {
 430     GList *scratch = NULL;
 431     struct match_data match_data = { NULL, NULL };
 432 
 433     // If dependent is assigned, only check there
 434     match_data.node = dependent->priv->fns->location(dependent, NULL,
 435                                                      pcmk__rsc_node_assigned);
 436     match_data.node = get_bundle_node_host(match_data.node);
 437     if (match_data.node != NULL) {
 438         pe__foreach_const_bundle_replica(bundle, match_replica_container,
 439                                          &match_data);
 440         return match_data.container;
 441     }
 442 
 443     // Otherwise, check for any of the dependent's allowed nodes
 444     scratch = g_hash_table_get_values(dependent->priv->allowed_nodes);
 445     scratch = pcmk__sort_nodes(scratch, NULL);
 446     for (const GList *iter = scratch; iter != NULL; iter = iter->next) {
 447         match_data.node = iter->data;
 448         match_data.node = get_bundle_node_host(match_data.node);
 449         if (match_data.node == NULL) {
 450             continue;
 451         }
 452 
 453         pe__foreach_const_bundle_replica(bundle, match_replica_container,
 454                                          &match_data);
 455         if (match_data.container != NULL) {
 456             break;
 457         }
 458     }
 459     g_list_free(scratch);
 460     return match_data.container;
 461 }
 462 
 463 struct coloc_data {
 464     const pcmk__colocation_t *colocation;
 465     pcmk_resource_t *dependent;
 466     GList *container_hosts;
 467     int priority_delta;
 468 };
 469 
 470 /*!
 471  * \internal
 472  * \brief Apply a colocation score to replica node scores or resource priority
 473  *
 474  * \param[in]     replica    Replica of primary bundle resource in colocation
 475  * \param[in,out] user_data  struct coloc_data for colocation being applied
 476  *
 477  * \return true (to indicate that any further replicas should be processed)
 478  */
 479 static bool
 480 replica_apply_coloc_score(const pcmk__bundle_replica_t *replica,
     /* [previous][next][first][last][top][bottom][index][help] */
 481                           void *user_data)
 482 {
 483     struct coloc_data *coloc_data = user_data;
 484     pcmk_node_t *chosen = NULL;
 485     pcmk_resource_t *container = replica->container;
 486 
 487     if (coloc_data->colocation->score < PCMK_SCORE_INFINITY) {
 488         int priority_delta =
 489             container->priv->cmds->apply_coloc_score(coloc_data->dependent,
 490                                                      container,
 491                                                      coloc_data->colocation,
 492                                                      false);
 493 
 494         coloc_data->priority_delta =
 495             pcmk__add_scores(coloc_data->priority_delta, priority_delta);
 496         return true;
 497     }
 498 
 499     chosen = container->priv->fns->location(container, NULL,
 500                                             pcmk__rsc_node_assigned);
 501     if ((chosen == NULL)
 502         || is_set_recursive(container, pcmk__rsc_blocked, true)) {
 503         return true;
 504     }
 505 
 506     if ((coloc_data->colocation->primary_role >= pcmk_role_promoted)
 507         && ((replica->child == NULL)
 508             || (replica->child->priv->next_role < pcmk_role_promoted))) {
 509         return true;
 510     }
 511 
 512     pcmk__rsc_trace(pe__const_top_resource(container, true),
 513                     "Allowing mandatory colocation %s using %s @%d",
 514                     coloc_data->colocation->id, pcmk__node_name(chosen),
 515                     chosen->assign->score);
 516     coloc_data->container_hosts = g_list_prepend(coloc_data->container_hosts,
 517                                                  chosen);
 518     return true;
 519 }
 520 
 521 /*!
 522  * \internal
 523  * \brief Apply a colocation's score to node scores or resource priority
 524  *
 525  * Given a colocation constraint, apply its score to the dependent's
 526  * allowed node scores (if we are still placing resources) or priority (if
 527  * we are choosing promotable clone instance roles).
 528  *
 529  * \param[in,out] dependent      Dependent resource in colocation
 530  * \param[in]     primary        Primary resource in colocation
 531  * \param[in]     colocation     Colocation constraint to apply
 532  * \param[in]     for_dependent  true if called on behalf of dependent
 533  *
 534  * \return The score added to the dependent's priority
 535  */
 536 int
 537 pcmk__bundle_apply_coloc_score(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
 538                                const pcmk_resource_t *primary,
 539                                const pcmk__colocation_t *colocation,
 540                                bool for_dependent)
 541 {
 542     struct coloc_data coloc_data = { colocation, dependent, NULL, 0 };
 543 
 544     /* This should never be called for the bundle itself as a dependent.
 545      * Instead, we add its colocation constraints to its containers and bundled
 546      * primitive and call the apply_coloc_score() method for them as dependents.
 547      */
 548     pcmk__assert(pcmk__is_bundle(primary) && pcmk__is_primitive(dependent)
 549                  && (colocation != NULL) && !for_dependent);
 550 
 551     if (pcmk_is_set(primary->flags, pcmk__rsc_unassigned)) {
 552         pcmk__rsc_trace(primary,
 553                         "Skipping applying colocation %s "
 554                         "because %s is still provisional",
 555                         colocation->id, primary->id);
 556         return 0;
 557     }
 558     pcmk__rsc_trace(primary, "Applying colocation %s (%s with %s at %s)",
 559                     colocation->id, dependent->id, primary->id,
 560                     pcmk_readable_score(colocation->score));
 561 
 562     /* If the constraint dependent is a clone or bundle, "dependent" here is one
 563      * of its instances. Look for a compatible instance of this bundle.
 564      */
 565     if (colocation->dependent->priv->variant > pcmk__rsc_variant_group) {
 566         const pcmk_resource_t *primary_container = NULL;
 567 
 568         primary_container = compatible_container(dependent, primary);
 569         if (primary_container != NULL) { // Success, we found one
 570             pcmk__rsc_debug(primary, "Pairing %s with %s",
 571                             dependent->id, primary_container->id);
 572 
 573             return dependent->priv->cmds->apply_coloc_score(dependent,
 574                                                             primary_container,
 575                                                             colocation, true);
 576         }
 577 
 578         if (colocation->score >= PCMK_SCORE_INFINITY) {
 579             // Failure, and it's fatal
 580             crm_notice("%s cannot run because there is no compatible "
 581                        "instance of %s to colocate with",
 582                        dependent->id, primary->id);
 583             pcmk__assign_resource(dependent, NULL, true, true);
 584 
 585         } else { // Failure, but we can ignore it
 586             pcmk__rsc_debug(primary,
 587                             "%s cannot be colocated with any instance of %s",
 588                             dependent->id, primary->id);
 589         }
 590         return 0;
 591     }
 592 
 593     pe__foreach_const_bundle_replica(primary, replica_apply_coloc_score,
 594                                      &coloc_data);
 595 
 596     if (colocation->score >= PCMK_SCORE_INFINITY) {
 597         pcmk__colocation_intersect_nodes(dependent, primary, colocation,
 598                                          coloc_data.container_hosts, false);
 599     }
 600     g_list_free(coloc_data.container_hosts);
 601     return coloc_data.priority_delta;
 602 }
 603 
 604 // Bundle implementation of pcmk__assignment_methods_t:with_this_colocations()
 605 void
 606 pcmk__with_bundle_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 607                               const pcmk_resource_t *orig_rsc, GList **list)
 608 {
 609     const pcmk_resource_t *bundled_rsc = NULL;
 610 
 611     pcmk__assert(pcmk__is_bundle(rsc) && (orig_rsc != NULL) && (list != NULL));
 612 
 613     // The bundle itself and its containers always get its colocations
 614     if ((orig_rsc == rsc)
 615         || pcmk_is_set(orig_rsc->flags, pcmk__rsc_replica_container)) {
 616 
 617         pcmk__add_with_this_list(list, rsc->priv->with_this_colocations,
 618                                  orig_rsc);
 619         return;
 620     }
 621 
 622     /* The bundled resource gets the colocations if it's promotable and we've
 623      * begun choosing roles
 624      */
 625     bundled_rsc = pe__bundled_resource(rsc);
 626     if ((bundled_rsc == NULL)
 627         || !pcmk_is_set(bundled_rsc->flags, pcmk__rsc_promotable)
 628         || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
 629         return;
 630     }
 631 
 632     if (orig_rsc == bundled_rsc) {
 633         if (pe__clone_flag_is_set(orig_rsc,
 634                                   pcmk__clone_promotion_constrained)) {
 635             /* orig_rsc is the clone and we're setting roles (or have already
 636              * done so)
 637              */
 638             pcmk__add_with_this_list(list, rsc->priv->with_this_colocations,
 639                                      orig_rsc);
 640         }
 641 
 642     } else if (!pcmk_is_set(orig_rsc->flags, pcmk__rsc_unassigned)) {
 643         /* orig_rsc is an instance and is already assigned. If something
 644          * requests colocations for orig_rsc now, it's for setting roles.
 645          */
 646         pcmk__add_with_this_list(list, rsc->priv->with_this_colocations,
 647                                  orig_rsc);
 648     }
 649 }
 650 
 651 // Bundle implementation of pcmk__assignment_methods_t:this_with_colocations()
 652 void
 653 pcmk__bundle_with_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 654                               const pcmk_resource_t *orig_rsc, GList **list)
 655 {
 656     const pcmk_resource_t *bundled_rsc = NULL;
 657 
 658     pcmk__assert(pcmk__is_bundle(rsc) && (orig_rsc != NULL) && (list != NULL));
 659 
 660     // The bundle itself and its containers always get its colocations
 661     if ((orig_rsc == rsc)
 662         || pcmk_is_set(orig_rsc->flags, pcmk__rsc_replica_container)) {
 663 
 664         pcmk__add_this_with_list(list, rsc->priv->this_with_colocations,
 665                                  orig_rsc);
 666         return;
 667     }
 668 
 669     /* The bundled resource gets the colocations if it's promotable and we've
 670      * begun choosing roles
 671      */
 672     bundled_rsc = pe__bundled_resource(rsc);
 673     if ((bundled_rsc == NULL)
 674         || !pcmk_is_set(bundled_rsc->flags, pcmk__rsc_promotable)
 675         || (pe__const_top_resource(orig_rsc, false) != bundled_rsc)) {
 676         return;
 677     }
 678 
 679     if (orig_rsc == bundled_rsc) {
 680         if (pe__clone_flag_is_set(orig_rsc,
 681                                   pcmk__clone_promotion_constrained)) {
 682             /* orig_rsc is the clone and we're setting roles (or have already
 683              * done so)
 684              */
 685             pcmk__add_this_with_list(list, rsc->priv->this_with_colocations,
 686                                      orig_rsc);
 687         }
 688 
 689     } else if (!pcmk_is_set(orig_rsc->flags, pcmk__rsc_unassigned)) {
 690         /* orig_rsc is an instance and is already assigned. If something
 691          * requests colocations for orig_rsc now, it's for setting roles.
 692          */
 693         pcmk__add_this_with_list(list, rsc->priv->this_with_colocations,
 694                                  orig_rsc);
 695     }
 696 }
 697 
 698 /*!
 699  * \internal
 700  * \brief Return action flags for a given bundle resource action
 701  *
 702  * \param[in,out] action  Bundle resource action to get flags for
 703  * \param[in]     node    If not NULL, limit effects to this node
 704  *
 705  * \return Flags appropriate to \p action on \p node
 706  */
 707 uint32_t
 708 pcmk__bundle_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 709 {
 710     GList *containers = NULL;
 711     uint32_t flags = 0;
 712     pcmk_resource_t *bundled_resource = NULL;
 713 
 714     pcmk__assert((action != NULL) && pcmk__is_bundle(action->rsc));
 715 
 716     bundled_resource = pe__bundled_resource(action->rsc);
 717     if (bundled_resource != NULL) {
 718         GList *children = bundled_resource->priv->children;
 719 
 720         // Clone actions are done on the bundled clone resource, not container
 721         switch (get_complex_task(bundled_resource, action->task)) {
 722             case pcmk__action_unspecified:
 723             case pcmk__action_notify:
 724             case pcmk__action_notified:
 725             case pcmk__action_promote:
 726             case pcmk__action_promoted:
 727             case pcmk__action_demote:
 728             case pcmk__action_demoted:
 729                 return pcmk__collective_action_flags(action, children, node);
 730             default:
 731                 break;
 732         }
 733     }
 734 
 735     containers = pe__bundle_containers(action->rsc);
 736     flags = pcmk__collective_action_flags(action, containers, node);
 737     g_list_free(containers);
 738     return flags;
 739 }
 740 
 741 /*!
 742  * \internal
 743  * \brief Apply a location constraint to a bundle replica
 744  *
 745  * \param[in,out] replica    Replica to apply constraint to
 746  * \param[in,out] user_data  Location constraint to apply
 747  *
 748  * \return true (to indicate that any further replicas should be processed)
 749  */
 750 static bool
 751 apply_location_to_replica(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 752 {
 753     pcmk__location_t *location = user_data;
 754 
 755     replica->container->priv->cmds->apply_location(replica->container,
 756                                                    location);
 757     if (replica->ip != NULL) {
 758         replica->ip->priv->cmds->apply_location(replica->ip, location);
 759     }
 760     return true;
 761 }
 762 
 763 /*!
 764  * \internal
 765  * \brief Apply a location constraint to a bundle resource's allowed node scores
 766  *
 767  * \param[in,out] rsc       Bundle resource to apply constraint to
 768  * \param[in,out] location  Location constraint to apply
 769  */
 770 void
 771 pcmk__bundle_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
     /* [previous][next][first][last][top][bottom][index][help] */
 772 {
 773     pcmk_resource_t *bundled_resource = NULL;
 774 
 775     pcmk__assert((location != NULL) && pcmk__is_bundle(rsc));
 776 
 777     pcmk__apply_location(rsc, location);
 778     pe__foreach_bundle_replica(rsc, apply_location_to_replica, location);
 779 
 780     bundled_resource = pe__bundled_resource(rsc);
 781     if ((bundled_resource != NULL)
 782         && ((location->role_filter == pcmk_role_unpromoted)
 783             || (location->role_filter == pcmk_role_promoted))) {
 784 
 785         bundled_resource->priv->cmds->apply_location(bundled_resource,
 786                                                      location);
 787         bundled_resource->priv->location_constraints =
 788             g_list_prepend(bundled_resource->priv->location_constraints,
 789                            location);
 790     }
 791 }
 792 
 793 #define XPATH_REMOTE "//nvpair[@name='" PCMK_REMOTE_RA_ADDR "']"
 794 
 795 /*!
 796  * \internal
 797  * \brief Add a bundle replica's actions to transition graph
 798  *
 799  * \param[in,out] replica    Replica to add to graph
 800  * \param[in]     user_data  Bundle that replica belongs to (for logging only)
 801  *
 802  * \return true (to indicate that any further replicas should be processed)
 803  */
 804 static bool
 805 add_replica_actions_to_graph(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 806 {
 807     if ((replica->remote != NULL)
 808         && pe__bundle_needs_remote_name(replica->remote)) {
 809 
 810         /* REMOTE_CONTAINER_HACK: Allow remote nodes to run containers that
 811          * run the remote executor inside, without needing a separate IP for
 812          * the container. This is done by configuring the inner remote's
 813          * connection host as the magic string "#uname", then
 814          * replacing it with the underlying host when needed.
 815          */
 816         xmlNode *nvpair = pcmk__xpath_find_one(replica->remote->priv->xml->doc,
 817                                                XPATH_REMOTE, LOG_ERR);
 818         const char *calculated_addr = NULL;
 819 
 820         // Replace the value in replica->remote->xml (if appropriate)
 821         calculated_addr = pe__add_bundle_remote_name(replica->remote, nvpair,
 822                                                      PCMK_XA_VALUE);
 823         if (calculated_addr != NULL) {
 824             /* Since this is for the bundle as a resource, and not any
 825              * particular action, replace the value in the default
 826              * parameters (not evaluated for node). create_graph_action()
 827              * will grab it from there to replace it in node-evaluated
 828              * parameters.
 829              */
 830             GHashTable *params = NULL;
 831 
 832             params = pe_rsc_params(replica->remote, NULL,
 833                                    replica->remote->priv->scheduler);
 834             pcmk__insert_dup(params, PCMK_REMOTE_RA_ADDR, calculated_addr);
 835         } else {
 836             pcmk_resource_t *bundle = user_data;
 837 
 838             /* The only way to get here is if the remote connection is
 839              * neither currently running nor scheduled to run. That means we
 840              * won't be doing any operations that require addr (only start
 841              * requires it; we additionally use it to compare digests when
 842              * unpacking status, promote, and migrate_from history, but
 843              * that's already happened by this point).
 844              */
 845             pcmk__rsc_info(bundle,
 846                            "Unable to determine address for bundle %s "
 847                            "remote connection", bundle->id);
 848         }
 849     }
 850     if (replica->ip != NULL) {
 851         replica->ip->priv->cmds->add_actions_to_graph(replica->ip);
 852     }
 853     replica->container->priv->cmds->add_actions_to_graph(replica->container);
 854     if (replica->remote != NULL) {
 855         replica->remote->priv->cmds->add_actions_to_graph(replica->remote);
 856     }
 857     return true;
 858 }
 859 
 860 /*!
 861  * \internal
 862  * \brief Add a bundle resource's actions to the transition graph
 863  *
 864  * \param[in,out] rsc  Bundle resource whose actions should be added
 865  */
 866 void
 867 pcmk__bundle_add_actions_to_graph(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 868 {
 869     pcmk_resource_t *bundled_resource = NULL;
 870 
 871     pcmk__assert(pcmk__is_bundle(rsc));
 872 
 873     bundled_resource = pe__bundled_resource(rsc);
 874     if (bundled_resource != NULL) {
 875         bundled_resource->priv->cmds->add_actions_to_graph(bundled_resource);
 876     }
 877     pe__foreach_bundle_replica(rsc, add_replica_actions_to_graph, rsc);
 878 }
 879 
 880 struct probe_data {
 881     pcmk_resource_t *bundle;    // Bundle being probed
 882     pcmk_node_t *node;          // Node to create probes on
 883     bool any_created;           // Whether any probes have been created
 884 };
 885 
 886 /*!
 887  * \internal
 888  * \brief Order a bundle replica's start after another replica's probe
 889  *
 890  * \param[in,out] replica    Replica to order start for
 891  * \param[in,out] user_data  Replica with probe to order after
 892  *
 893  * \return true (to indicate that any further replicas should be processed)
 894  */
 895 static bool
 896 order_replica_start_after(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 897 {
 898     pcmk__bundle_replica_t *probed_replica = user_data;
 899 
 900     if ((replica == probed_replica) || (replica->container == NULL)) {
 901         return true;
 902     }
 903     pcmk__new_ordering(probed_replica->container,
 904                        pcmk__op_key(probed_replica->container->id,
 905                                     PCMK_ACTION_MONITOR, 0),
 906                        NULL, replica->container,
 907                        pcmk__op_key(replica->container->id, PCMK_ACTION_START,
 908                                     0),
 909                        NULL, pcmk__ar_ordered|pcmk__ar_if_on_same_node,
 910                        replica->container->priv->scheduler);
 911     return true;
 912 }
 913 
 914 /*!
 915  * \internal
 916  * \brief Create probes for a bundle replica's resources
 917  *
 918  * \param[in,out] replica    Replica to create probes for
 919  * \param[in,out] user_data  struct probe_data
 920  *
 921  * \return true (to indicate that any further replicas should be processed)
 922  */
 923 static bool
 924 create_replica_probes(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 925 {
 926     struct probe_data *probe_data = user_data;
 927     pcmk_resource_t *bundle = probe_data->bundle;
 928 
 929     /* This is guaranteed when constructing probe_data in pcmk__bundle_create_probe,
 930      * but this makes static analysis happy.
 931      */
 932     pcmk__assert(bundle != NULL);
 933 
 934     if ((replica->ip != NULL)
 935         && replica->ip->priv->cmds->create_probe(replica->ip,
 936                                                  probe_data->node)) {
 937         probe_data->any_created = true;
 938     }
 939     if ((replica->child != NULL)
 940         && pcmk__same_node(probe_data->node, replica->node)
 941         && replica->child->priv->cmds->create_probe(replica->child,
 942                                                     probe_data->node)) {
 943         probe_data->any_created = true;
 944     }
 945     if (replica->container->priv->cmds->create_probe(replica->container,
 946                                                      probe_data->node)) {
 947         probe_data->any_created = true;
 948 
 949         /* If we're limited to one replica per host (due to
 950          * the lack of an IP range probably), then we don't
 951          * want any of our peer containers starting until
 952          * we've established that no other copies are already
 953          * running.
 954          *
 955          * Partly this is to ensure that the maximum replicas per host is
 956          * observed, but also to ensure that the containers
 957          * don't fail to start because the necessary port
 958          * mappings (which won't include an IP for uniqueness)
 959          * are already taken
 960          */
 961         if (bundle->priv->fns->max_per_node(bundle) == 1) {
 962             pe__foreach_bundle_replica(bundle, order_replica_start_after,
 963                                        replica);
 964         }
 965     }
 966     if ((replica->remote != NULL)
 967         && replica->remote->priv->cmds->create_probe(replica->remote,
 968                                                      probe_data->node)) {
 969         /* Do not probe the remote resource until we know where the container is
 970          * running. This is required for REMOTE_CONTAINER_HACK to correctly
 971          * probe remote resources.
 972          */
 973         char *probe_uuid = pcmk__op_key(replica->remote->id,
 974                                         PCMK_ACTION_MONITOR, 0);
 975         pcmk_action_t *probe = NULL;
 976 
 977         probe = find_first_action(replica->remote->priv->actions, probe_uuid,
 978                                   NULL, probe_data->node);
 979         free(probe_uuid);
 980         if (probe != NULL) {
 981             probe_data->any_created = true;
 982             pcmk__rsc_trace(bundle, "Ordering %s probe on %s",
 983                             replica->remote->id,
 984                             pcmk__node_name(probe_data->node));
 985             pcmk__new_ordering(replica->container,
 986                                pcmk__op_key(replica->container->id,
 987                                             PCMK_ACTION_START, 0),
 988                                NULL, replica->remote, NULL, probe,
 989                                pcmk__ar_nested_remote_probe,
 990                                bundle->priv->scheduler);
 991         }
 992     }
 993     return true;
 994 }
 995 
 996 /*!
 997  * \internal
 998  *
 999  * \brief Schedule any probes needed for a bundle resource on a node
1000  *
1001  * \param[in,out] rsc   Bundle resource to create probes for
1002  * \param[in,out] node  Node to create probe on
1003  *
1004  * \return true if any probe was created, otherwise false
1005  */
1006 bool
1007 pcmk__bundle_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1008 {
1009     struct probe_data probe_data = { rsc, node, false };
1010 
1011     pcmk__assert(pcmk__is_bundle(rsc));
1012     pe__foreach_bundle_replica(rsc, create_replica_probes, &probe_data);
1013     return probe_data.any_created;
1014 }
1015 
1016 /*!
1017  * \internal
1018  * \brief Output actions for one bundle replica
1019  *
1020  * \param[in,out] replica    Replica to output actions for
1021  * \param[in]     user_data  Unused
1022  *
1023  * \return true (to indicate that any further replicas should be processed)
1024  */
1025 static bool
1026 output_replica_actions(pcmk__bundle_replica_t *replica, void *user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1027 {
1028     if (replica->ip != NULL) {
1029         replica->ip->priv->cmds->output_actions(replica->ip);
1030     }
1031     replica->container->priv->cmds->output_actions(replica->container);
1032     if (replica->remote != NULL) {
1033         replica->remote->priv->cmds->output_actions(replica->remote);
1034     }
1035     if (replica->child != NULL) {
1036         replica->child->priv->cmds->output_actions(replica->child);
1037     }
1038     return true;
1039 }
1040 
1041 /*!
1042  * \internal
1043  * \brief Output a summary of scheduled actions for a bundle resource
1044  *
1045  * \param[in,out] rsc  Bundle resource to output actions for
1046  */
1047 void
1048 pcmk__output_bundle_actions(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1049 {
1050     pcmk__assert(pcmk__is_bundle(rsc));
1051     pe__foreach_bundle_replica(rsc, output_replica_actions, NULL);
1052 }
1053 
1054 // Bundle implementation of pcmk__assignment_methods_t:add_utilization()
1055 void
1056 pcmk__bundle_add_utilization(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1057                              const pcmk_resource_t *orig_rsc, GList *all_rscs,
1058                              GHashTable *utilization)
1059 {
1060     pcmk_resource_t *container = NULL;
1061 
1062     pcmk__assert(pcmk__is_bundle(rsc));
1063 
1064     if (!pcmk_is_set(rsc->flags, pcmk__rsc_unassigned)) {
1065         return;
1066     }
1067 
1068     /* All bundle replicas are identical, so using the utilization of the first
1069      * is sufficient for any. Only the implicit container resource can have
1070      * utilization values.
1071      */
1072     container = pe__first_container(rsc);
1073     if (container != NULL) {
1074         container->priv->cmds->add_utilization(container, orig_rsc, all_rscs,
1075                                                utilization);
1076     }
1077 }
1078 
1079 // Bundle implementation of pcmk__assignment_methods_t:shutdown_lock()
1080 void
1081 pcmk__bundle_shutdown_lock(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1082 {
1083     pcmk__assert(pcmk__is_bundle(rsc));
1084     // Bundles currently don't support shutdown locks
1085 }

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