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

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