root/lib/pacemaker/pcmk_sched_clone.c

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

DEFINITIONS

This source file includes following definitions.
  1. pcmk__clone_assign
  2. pcmk__clone_create_actions
  3. pcmk__clone_internal_constraints
  4. can_interleave
  5. pcmk__clone_apply_coloc_score
  6. pcmk__with_clone_colocations
  7. pcmk__clone_with_colocations
  8. pcmk__clone_action_flags
  9. pcmk__clone_apply_location
  10. call_action_flags
  11. pcmk__clone_add_actions_to_graph
  12. rsc_probed_on
  13. find_probed_instance_on
  14. probe_anonymous_clone
  15. pcmk__clone_create_probe
  16. pcmk__clone_add_graph_meta
  17. pcmk__clone_add_utilization
  18. pcmk__clone_shutdown_lock

   1 /*
   2  * Copyright 2004-2023 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <crm/msg_xml.h>
  13 #include <pacemaker-internal.h>
  14 
  15 #include "libpacemaker_private.h"
  16 
  17 /*!
  18  * \internal
  19  * \brief Assign a clone resource's instances to nodes
  20  *
  21  * \param[in,out] rsc           Clone resource to assign
  22  * \param[in]     prefer        Node to prefer, if all else is equal
  23  * \param[in]     stop_if_fail  If \c true and a primitive descendant of \p rsc
  24  *                              can't be assigned to a node, set the
  25  *                              descendant's next role to stopped and update
  26  *                              existing actions
  27  *
  28  * \return NULL (clones are not assigned to a single node)
  29  *
  30  * \note If \p stop_if_fail is \c false, then \c pcmk__unassign_resource() can
  31  *       completely undo the assignment. A successful assignment can be either
  32  *       undone or left alone as final. A failed assignment has the same effect
  33  *       as calling pcmk__unassign_resource(); there are no side effects on
  34  *       roles or actions.
  35  */
  36 pcmk_node_t *
  37 pcmk__clone_assign(pcmk_resource_t *rsc, const pcmk_node_t *prefer,
     /* [previous][next][first][last][top][bottom][index][help] */
  38                    bool stop_if_fail)
  39 {
  40     GList *colocations = NULL;
  41 
  42     CRM_ASSERT(pe_rsc_is_clone(rsc));
  43 
  44     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
  45         return NULL; // Assignment has already been done
  46     }
  47 
  48     // Detect assignment loops
  49     if (pcmk_is_set(rsc->flags, pcmk_rsc_assigning)) {
  50         pe_rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id);
  51         return NULL;
  52     }
  53     pe__set_resource_flags(rsc, pcmk_rsc_assigning);
  54 
  55     // If this clone is promotable, consider nodes' promotion scores
  56     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
  57         pcmk__add_promotion_scores(rsc);
  58     }
  59 
  60     // If this clone is colocated with any other resources, assign those first
  61     colocations = pcmk__this_with_colocations(rsc);
  62     for (GList *iter = colocations; iter != NULL; iter = iter->next) {
  63         pcmk__colocation_t *constraint = (pcmk__colocation_t *) iter->data;
  64 
  65         pe_rsc_trace(rsc, "%s: Assigning colocation %s primary %s first",
  66                      rsc->id, constraint->id, constraint->primary->id);
  67         constraint->primary->cmds->assign(constraint->primary, prefer,
  68                                           stop_if_fail);
  69     }
  70     g_list_free(colocations);
  71 
  72     // If any resources are colocated with this one, consider their preferences
  73     colocations = pcmk__with_this_colocations(rsc);
  74     g_list_foreach(colocations, pcmk__add_dependent_scores, rsc);
  75     g_list_free(colocations);
  76 
  77     pe__show_node_scores(!pcmk_is_set(rsc->cluster->flags,
  78                                       pcmk_sched_output_scores),
  79                          rsc, __func__, rsc->allowed_nodes, rsc->cluster);
  80 
  81     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance);
  82     pcmk__assign_instances(rsc, rsc->children, pe__clone_max(rsc),
  83                            pe__clone_node_max(rsc));
  84 
  85     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
  86         pcmk__set_instance_roles(rsc);
  87     }
  88 
  89     pe__clear_resource_flags(rsc, pcmk_rsc_unassigned|pcmk_rsc_assigning);
  90     pe_rsc_trace(rsc, "Assigned clone %s", rsc->id);
  91     return NULL;
  92 }
  93 
  94 /*!
  95  * \internal
  96  * \brief Create all actions needed for a given clone resource
  97  *
  98  * \param[in,out] rsc  Clone resource to create actions for
  99  */
 100 void
 101 pcmk__clone_create_actions(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103     CRM_ASSERT(pe_rsc_is_clone(rsc));
 104 
 105     pe_rsc_trace(rsc, "Creating actions for clone %s", rsc->id);
 106     pcmk__create_instance_actions(rsc, rsc->children);
 107     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
 108         pcmk__create_promotable_actions(rsc);
 109     }
 110 }
 111 
 112 /*!
 113  * \internal
 114  * \brief Create implicit constraints needed for a clone resource
 115  *
 116  * \param[in,out] rsc  Clone resource to create implicit constraints for
 117  */
 118 void
 119 pcmk__clone_internal_constraints(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 120 {
 121     bool ordered = false;
 122 
 123     CRM_ASSERT(pe_rsc_is_clone(rsc));
 124 
 125     pe_rsc_trace(rsc, "Creating internal constraints for clone %s", rsc->id);
 126 
 127     // Restart ordering: Stop -> stopped -> start -> started
 128     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
 129                                  rsc, PCMK_ACTION_START,
 130                                  pcmk__ar_ordered);
 131     pcmk__order_resource_actions(rsc, PCMK_ACTION_START,
 132                                  rsc, PCMK_ACTION_RUNNING,
 133                                  pcmk__ar_unrunnable_first_blocks);
 134     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOP,
 135                                  rsc, PCMK_ACTION_STOPPED,
 136                                  pcmk__ar_unrunnable_first_blocks);
 137 
 138     // Demoted -> stop and started -> promote
 139     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
 140         pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
 141                                      rsc, PCMK_ACTION_STOP,
 142                                      pcmk__ar_ordered);
 143         pcmk__order_resource_actions(rsc, PCMK_ACTION_RUNNING,
 144                                      rsc, PCMK_ACTION_PROMOTE,
 145                                      pcmk__ar_unrunnable_first_blocks);
 146     }
 147 
 148     ordered = pe__clone_is_ordered(rsc);
 149     if (ordered) {
 150         /* Ordered clone instances must start and stop by instance number. The
 151          * instances might have been previously shuffled for assignment or
 152          * promotion purposes, so re-sort them.
 153          */
 154         rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
 155     }
 156     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 157         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
 158 
 159         instance->cmds->internal_constraints(instance);
 160 
 161         // Start clone -> start instance -> clone started
 162         pcmk__order_starts(rsc, instance, pcmk__ar_unrunnable_first_blocks
 163                                           |pcmk__ar_then_implies_first_graphed);
 164         pcmk__order_resource_actions(instance, PCMK_ACTION_START,
 165                                      rsc, PCMK_ACTION_RUNNING,
 166                                      pcmk__ar_first_implies_then_graphed);
 167 
 168         // Stop clone -> stop instance -> clone stopped
 169         pcmk__order_stops(rsc, instance, pcmk__ar_then_implies_first_graphed);
 170         pcmk__order_resource_actions(instance, PCMK_ACTION_STOP,
 171                                      rsc, PCMK_ACTION_STOPPED,
 172                                      pcmk__ar_first_implies_then_graphed);
 173 
 174         /* Instances of ordered clones must be started and stopped by instance
 175          * number. Since only some instances may be starting or stopping, order
 176          * each instance relative to every later instance.
 177          */
 178         if (ordered) {
 179             for (GList *later = iter->next;
 180                  later != NULL; later = later->next) {
 181                 pcmk__order_starts(instance, (pcmk_resource_t *) later->data,
 182                                    pcmk__ar_ordered);
 183                 pcmk__order_stops((pcmk_resource_t *) later->data, instance,
 184                                   pcmk__ar_ordered);
 185             }
 186         }
 187     }
 188     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
 189         pcmk__order_promotable_instances(rsc);
 190     }
 191 }
 192 
 193 /*!
 194  * \internal
 195  * \brief Check whether colocated resources can be interleaved
 196  *
 197  * \param[in] colocation  Colocation constraint with clone as primary
 198  *
 199  * \return true if colocated resources can be interleaved, otherwise false
 200  */
 201 static bool
 202 can_interleave(const pcmk__colocation_t *colocation)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204     const pcmk_resource_t *dependent = colocation->dependent;
 205 
 206     // Only colocations between clone or bundle resources use interleaving
 207     if (dependent->variant <= pcmk_rsc_variant_group) {
 208         return false;
 209     }
 210 
 211     // Only the dependent needs to be marked for interleaving
 212     if (!crm_is_true(g_hash_table_lookup(dependent->meta,
 213                                          XML_RSC_ATTR_INTERLEAVE))) {
 214         return false;
 215     }
 216 
 217     /* @TODO Do we actually care about multiple primary instances sharing a
 218      * dependent instance?
 219      */
 220     if (dependent->fns->max_per_node(dependent)
 221         != colocation->primary->fns->max_per_node(colocation->primary)) {
 222         pcmk__config_err("Cannot interleave %s and %s because they do not "
 223                          "support the same number of instances per node",
 224                          dependent->id, colocation->primary->id);
 225         return false;
 226     }
 227 
 228     return true;
 229 }
 230 
 231 /*!
 232  * \internal
 233  * \brief Apply a colocation's score to node scores or resource priority
 234  *
 235  * Given a colocation constraint, apply its score to the dependent's
 236  * allowed node scores (if we are still placing resources) or priority (if
 237  * we are choosing promotable clone instance roles).
 238  *
 239  * \param[in,out] dependent      Dependent resource in colocation
 240  * \param[in]     primary        Primary resource in colocation
 241  * \param[in]     colocation     Colocation constraint to apply
 242  * \param[in]     for_dependent  true if called on behalf of dependent
 243  */
 244 void
 245 pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
 246                               const pcmk_resource_t *primary,
 247                               const pcmk__colocation_t *colocation,
 248                               bool for_dependent)
 249 {
 250     const GList *iter = NULL;
 251 
 252     /* This should never be called for the clone itself as a dependent. Instead,
 253      * we add its colocation constraints to its instances and call the
 254      * apply_coloc_score() method for the instances as dependents.
 255      */
 256     CRM_ASSERT(!for_dependent);
 257 
 258     CRM_ASSERT((colocation != NULL) && pe_rsc_is_clone(primary)
 259                && (dependent != NULL)
 260                && (dependent->variant == pcmk_rsc_variant_primitive));
 261 
 262     if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
 263         pe_rsc_trace(primary,
 264                      "Delaying processing colocation %s "
 265                      "because cloned primary %s is still provisional",
 266                      colocation->id, primary->id);
 267         return;
 268     }
 269 
 270     pe_rsc_trace(primary, "Processing colocation %s (%s with clone %s @%s)",
 271                  colocation->id, dependent->id, primary->id,
 272                  pcmk_readable_score(colocation->score));
 273 
 274     // Apply role-specific colocations
 275     if (pcmk_is_set(primary->flags, pcmk_rsc_promotable)
 276         && (colocation->primary_role != pcmk_role_unknown)) {
 277 
 278         if (pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
 279             // We're assigning the dependent to a node
 280             pcmk__update_dependent_with_promotable(primary, dependent,
 281                                                    colocation);
 282             return;
 283         }
 284 
 285         if (colocation->dependent_role == pcmk_role_promoted) {
 286             // We're choosing a role for the dependent
 287             pcmk__update_promotable_dependent_priority(primary, dependent,
 288                                                        colocation);
 289             return;
 290         }
 291     }
 292 
 293     // Apply interleaved colocations
 294     if (can_interleave(colocation)) {
 295         const pcmk_resource_t *primary_instance = NULL;
 296 
 297         primary_instance = pcmk__find_compatible_instance(dependent, primary,
 298                                                           pcmk_role_unknown,
 299                                                           false);
 300         if (primary_instance != NULL) {
 301             pe_rsc_debug(primary, "Interleaving %s with %s",
 302                          dependent->id, primary_instance->id);
 303             dependent->cmds->apply_coloc_score(dependent, primary_instance,
 304                                                colocation, true);
 305 
 306         } else if (colocation->score >= INFINITY) {
 307             crm_notice("%s cannot run because it cannot interleave with "
 308                        "any instance of %s", dependent->id, primary->id);
 309             pcmk__assign_resource(dependent, NULL, true, true);
 310 
 311         } else {
 312             pe_rsc_debug(primary,
 313                          "%s will not colocate with %s "
 314                          "because no instance can interleave with it",
 315                          dependent->id, primary->id);
 316         }
 317 
 318         return;
 319     }
 320 
 321     // Apply mandatory colocations
 322     if (colocation->score >= INFINITY) {
 323         GList *primary_nodes = NULL;
 324 
 325         // Dependent can run only where primary will have unblocked instances
 326         for (iter = primary->children; iter != NULL; iter = iter->next) {
 327             const pcmk_resource_t *instance = iter->data;
 328             pcmk_node_t *chosen = instance->fns->location(instance, NULL, 0);
 329 
 330             if ((chosen != NULL)
 331                 && !is_set_recursive(instance, pcmk_rsc_blocked, TRUE)) {
 332                 pe_rsc_trace(primary, "Allowing %s: %s %d",
 333                              colocation->id, pe__node_name(chosen),
 334                              chosen->weight);
 335                 primary_nodes = g_list_prepend(primary_nodes, chosen);
 336             }
 337         }
 338         pcmk__colocation_intersect_nodes(dependent, primary, colocation,
 339                                          primary_nodes, false);
 340         g_list_free(primary_nodes);
 341         return;
 342     }
 343 
 344     // Apply optional colocations
 345     for (iter = primary->children; iter != NULL; iter = iter->next) {
 346         const pcmk_resource_t *instance = iter->data;
 347 
 348         instance->cmds->apply_coloc_score(dependent, instance, colocation,
 349                                           false);
 350     }
 351 }
 352 
 353 // Clone implementation of pcmk_assignment_methods_t:with_this_colocations()
 354 void
 355 pcmk__with_clone_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 356                              const pcmk_resource_t *orig_rsc, GList **list)
 357 {
 358     CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
 359 
 360     pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
 361 
 362     if (rsc->parent != NULL) {
 363         rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc, list);
 364     }
 365 }
 366 
 367 // Clone implementation of pcmk_assignment_methods_t:this_with_colocations()
 368 void
 369 pcmk__clone_with_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 370                              const pcmk_resource_t *orig_rsc, GList **list)
 371 {
 372     CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
 373 
 374     pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
 375 
 376     if (rsc->parent != NULL) {
 377         rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc, list);
 378     }
 379 }
 380 
 381 /*!
 382  * \internal
 383  * \brief Return action flags for a given clone resource action
 384  *
 385  * \param[in,out] action  Action to get flags for
 386  * \param[in]     node    If not NULL, limit effects to this node
 387  *
 388  * \return Flags appropriate to \p action on \p node
 389  */
 390 uint32_t
 391 pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 392 {
 393     CRM_ASSERT((action != NULL) && pe_rsc_is_clone(action->rsc));
 394 
 395     return pcmk__collective_action_flags(action, action->rsc->children, node);
 396 }
 397 
 398 /*!
 399  * \internal
 400  * \brief Apply a location constraint to a clone resource's allowed node scores
 401  *
 402  * \param[in,out] rsc       Clone resource to apply constraint to
 403  * \param[in,out] location  Location constraint to apply
 404  */
 405 void
 406 pcmk__clone_apply_location(pcmk_resource_t *rsc, pe__location_t *location)
     /* [previous][next][first][last][top][bottom][index][help] */
 407 {
 408     CRM_CHECK((location != NULL) && pe_rsc_is_clone(rsc), return);
 409 
 410     pcmk__apply_location(rsc, location);
 411 
 412     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 413         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
 414 
 415         instance->cmds->apply_location(instance, location);
 416     }
 417 }
 418 
 419 // GFunc wrapper for calling the action_flags() resource method
 420 static void
 421 call_action_flags(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 422 {
 423     pcmk_resource_t *rsc = user_data;
 424 
 425     rsc->cmds->action_flags((pcmk_action_t *) data, NULL);
 426 }
 427 
 428 /*!
 429  * \internal
 430  * \brief Add a clone resource's actions to the transition graph
 431  *
 432  * \param[in,out] rsc  Resource whose actions should be added
 433  */
 434 void
 435 pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 436 {
 437     CRM_ASSERT(pe_rsc_is_clone(rsc));
 438 
 439     g_list_foreach(rsc->actions, call_action_flags, rsc);
 440     pe__create_clone_notifications(rsc);
 441 
 442     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 443         pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
 444 
 445         child_rsc->cmds->add_actions_to_graph(child_rsc);
 446     }
 447 
 448     pcmk__add_rsc_actions_to_graph(rsc);
 449     pe__free_clone_notification_data(rsc);
 450 }
 451 
 452 /*!
 453  * \internal
 454  * \brief Check whether a resource or any children have been probed on a node
 455  *
 456  * \param[in] rsc   Resource to check
 457  * \param[in] node  Node to check
 458  *
 459  * \return true if \p node is in the known_on table of \p rsc or any of its
 460  *         children, otherwise false
 461  */
 462 static bool
 463 rsc_probed_on(const pcmk_resource_t *rsc, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 464 {
 465     if (rsc->children != NULL) {
 466         for (GList *child_iter = rsc->children; child_iter != NULL;
 467              child_iter = child_iter->next) {
 468 
 469             pcmk_resource_t *child = (pcmk_resource_t *) child_iter->data;
 470 
 471             if (rsc_probed_on(child, node)) {
 472                 return true;
 473             }
 474         }
 475         return false;
 476     }
 477 
 478     if (rsc->known_on != NULL) {
 479         GHashTableIter iter;
 480         pcmk_node_t *known_node = NULL;
 481 
 482         g_hash_table_iter_init(&iter, rsc->known_on);
 483         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &known_node)) {
 484             if (pe__same_node(node, known_node)) {
 485                 return true;
 486             }
 487         }
 488     }
 489     return false;
 490 }
 491 
 492 /*!
 493  * \internal
 494  * \brief Find clone instance that has been probed on given node
 495  *
 496  * \param[in] clone  Clone resource to check
 497  * \param[in] node   Node to check
 498  *
 499  * \return Instance of \p clone that has been probed on \p node if any,
 500  *         otherwise NULL
 501  */
 502 static pcmk_resource_t *
 503 find_probed_instance_on(const pcmk_resource_t *clone, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 504 {
 505     for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
 506         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
 507 
 508         if (rsc_probed_on(instance, node)) {
 509             return instance;
 510         }
 511     }
 512     return NULL;
 513 }
 514 
 515 /*!
 516  * \internal
 517  * \brief Probe an anonymous clone on a node
 518  *
 519  * \param[in,out] clone  Anonymous clone to probe
 520  * \param[in,out] node   Node to probe \p clone on
 521  */
 522 static bool
 523 probe_anonymous_clone(pcmk_resource_t *clone, pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 524 {
 525     // Check whether we already probed an instance on this node
 526     pcmk_resource_t *child = find_probed_instance_on(clone, node);
 527 
 528     // Otherwise, check if we plan to start an instance on this node
 529     for (GList *iter = clone->children; (iter != NULL) && (child == NULL);
 530          iter = iter->next) {
 531         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
 532         const pcmk_node_t *instance_node = NULL;
 533 
 534         instance_node = instance->fns->location(instance, NULL, 0);
 535         if (pe__same_node(instance_node, node)) {
 536             child = instance;
 537         }
 538     }
 539 
 540     // Otherwise, use the first clone instance
 541     if (child == NULL) {
 542         child = clone->children->data;
 543     }
 544 
 545     // Anonymous clones only need to probe a single instance
 546     return child->cmds->create_probe(child, node);
 547 }
 548 
 549 /*!
 550  * \internal
 551  * \brief Schedule any probes needed for a resource on a node
 552  *
 553  * \param[in,out] rsc   Resource to create probe for
 554  * \param[in,out] node  Node to create probe on
 555  *
 556  * \return true if any probe was created, otherwise false
 557  */
 558 bool
 559 pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 560 {
 561     CRM_ASSERT((node != NULL) && pe_rsc_is_clone(rsc));
 562 
 563     if (rsc->exclusive_discover) {
 564         /* The clone is configured to be probed only where a location constraint
 565          * exists with resource-discovery set to exclusive.
 566          *
 567          * This check is not strictly necessary here since the instance's
 568          * create_probe() method would also check, but doing it here is more
 569          * efficient (especially for unique clones with a large number of
 570          * instances), and affects the CRM_meta_notify_available_uname variable
 571          * passed with notify actions.
 572          */
 573         pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes,
 574                                                    node->details->id);
 575 
 576         if ((allowed == NULL)
 577             || (allowed->rsc_discover_mode != pcmk_probe_exclusive)) {
 578             /* This node is not marked for resource discovery. Remove it from
 579              * allowed_nodes so that notifications contain only nodes that the
 580              * clone can possibly run on.
 581              */
 582             pe_rsc_trace(rsc,
 583                          "Skipping probe for %s on %s because resource has "
 584                          "exclusive discovery but is not allowed on node",
 585                          rsc->id, pe__node_name(node));
 586             g_hash_table_remove(rsc->allowed_nodes, node->details->id);
 587             return false;
 588         }
 589     }
 590 
 591     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
 592     if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
 593         return pcmk__probe_resource_list(rsc->children, node);
 594     } else {
 595         return probe_anonymous_clone(rsc, node);
 596     }
 597 }
 598 
 599 /*!
 600  * \internal
 601  * \brief Add meta-attributes relevant to transition graph actions to XML
 602  *
 603  * Add clone-specific meta-attributes needed for transition graph actions.
 604  *
 605  * \param[in]     rsc  Clone resource whose meta-attributes should be added
 606  * \param[in,out] xml  Transition graph action attributes XML to add to
 607  */
 608 void
 609 pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 610 {
 611     char *name = NULL;
 612 
 613     CRM_ASSERT(pe_rsc_is_clone(rsc) && (xml != NULL));
 614 
 615     name = crm_meta_name(XML_RSC_ATTR_UNIQUE);
 616     crm_xml_add(xml, name, pe__rsc_bool_str(rsc, pcmk_rsc_unique));
 617     free(name);
 618 
 619     name = crm_meta_name(XML_RSC_ATTR_NOTIFY);
 620     crm_xml_add(xml, name, pe__rsc_bool_str(rsc, pcmk_rsc_notify));
 621     free(name);
 622 
 623     name = crm_meta_name(PCMK_META_CLONE_MAX);
 624     crm_xml_add_int(xml, name, pe__clone_max(rsc));
 625     free(name);
 626 
 627     name = crm_meta_name(PCMK_META_CLONE_NODE_MAX);
 628     crm_xml_add_int(xml, name, pe__clone_node_max(rsc));
 629     free(name);
 630 
 631     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
 632         int promoted_max = pe__clone_promoted_max(rsc);
 633         int promoted_node_max = pe__clone_promoted_node_max(rsc);
 634 
 635         name = crm_meta_name(PCMK_META_PROMOTED_MAX);
 636         crm_xml_add_int(xml, name, promoted_max);
 637         free(name);
 638 
 639         name = crm_meta_name(PCMK_META_PROMOTED_NODE_MAX);
 640         crm_xml_add_int(xml, name, promoted_node_max);
 641         free(name);
 642 
 643         /* @COMPAT Maintain backward compatibility with resource agents that
 644          * expect the old names (deprecated since 2.0.0).
 645          */
 646         name = crm_meta_name(PCMK_XA_PROMOTED_MAX_LEGACY);
 647         crm_xml_add_int(xml, name, promoted_max);
 648         free(name);
 649 
 650         name = crm_meta_name(PCMK_XA_PROMOTED_NODE_MAX_LEGACY);
 651         crm_xml_add_int(xml, name, promoted_node_max);
 652         free(name);
 653     }
 654 }
 655 
 656 // Clone implementation of pcmk_assignment_methods_t:add_utilization()
 657 void
 658 pcmk__clone_add_utilization(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 659                             const pcmk_resource_t *orig_rsc, GList *all_rscs,
 660                             GHashTable *utilization)
 661 {
 662     bool existing = false;
 663     pcmk_resource_t *child = NULL;
 664 
 665     CRM_ASSERT(pe_rsc_is_clone(rsc) && (orig_rsc != NULL)
 666                && (utilization != NULL));
 667 
 668     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
 669         return;
 670     }
 671 
 672     // Look for any child already existing in the list
 673     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 674         child = (pcmk_resource_t *) iter->data;
 675         if (g_list_find(all_rscs, child)) {
 676             existing = true; // Keep checking remaining children
 677         } else {
 678             // If this is a clone of a group, look for group's members
 679             for (GList *member_iter = child->children; member_iter != NULL;
 680                  member_iter = member_iter->next) {
 681 
 682                 pcmk_resource_t *member = (pcmk_resource_t *) member_iter->data;
 683 
 684                 if (g_list_find(all_rscs, member) != NULL) {
 685                     // Add *child's* utilization, not group member's
 686                     child->cmds->add_utilization(child, orig_rsc, all_rscs,
 687                                                  utilization);
 688                     existing = true;
 689                     break;
 690                 }
 691             }
 692         }
 693     }
 694 
 695     if (!existing && (rsc->children != NULL)) {
 696         // If nothing was found, still add first child's utilization
 697         child = (pcmk_resource_t *) rsc->children->data;
 698 
 699         child->cmds->add_utilization(child, orig_rsc, all_rscs, utilization);
 700     }
 701 }
 702 
 703 // Clone implementation of pcmk_assignment_methods_t:shutdown_lock()
 704 void
 705 pcmk__clone_shutdown_lock(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 706 {
 707     CRM_ASSERT(pe_rsc_is_clone(rsc));
 708     return; // Clones currently don't support shutdown locks
 709 }

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