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

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