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-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 <crm/common/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(pcmk__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         pcmk__rsc_debug(rsc, "Breaking assignment loop involving %s", rsc->id);
  51         return NULL;
  52     }
  53     pcmk__set_rsc_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         pcmk__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     pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned|pcmk_rsc_assigning);
  90     pcmk__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(pcmk__is_clone(rsc));
 104 
 105     pcmk__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(pcmk__is_clone(rsc));
 124 
 125     pcmk__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                                          PCMK_META_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  * \return The score added to the dependent's priority
 245  */
 246 int
 247 pcmk__clone_apply_coloc_score(pcmk_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
 248                               const pcmk_resource_t *primary,
 249                               const pcmk__colocation_t *colocation,
 250                               bool for_dependent)
 251 {
 252     const GList *iter = NULL;
 253     int priority_delta = 0;
 254 
 255     /* This should never be called for the clone itself as a dependent. Instead,
 256      * we add its colocation constraints to its instances and call the
 257      * apply_coloc_score() method for the instances as dependents.
 258      */
 259     CRM_ASSERT(!for_dependent);
 260 
 261     CRM_ASSERT((colocation != NULL) && pcmk__is_clone(primary)
 262                && pcmk__is_primitive(dependent));
 263 
 264     if (pcmk_is_set(primary->flags, pcmk_rsc_unassigned)) {
 265         pcmk__rsc_trace(primary,
 266                         "Delaying processing colocation %s "
 267                         "because cloned primary %s is still provisional",
 268                         colocation->id, primary->id);
 269         return 0;
 270     }
 271 
 272     pcmk__rsc_trace(primary, "Processing colocation %s (%s with clone %s @%s)",
 273                     colocation->id, dependent->id, primary->id,
 274                     pcmk_readable_score(colocation->score));
 275 
 276     // Apply role-specific colocations
 277     if (pcmk_is_set(primary->flags, pcmk_rsc_promotable)
 278         && (colocation->primary_role != pcmk_role_unknown)) {
 279 
 280         if (pcmk_is_set(dependent->flags, pcmk_rsc_unassigned)) {
 281             // We're assigning the dependent to a node
 282             pcmk__update_dependent_with_promotable(primary, dependent,
 283                                                    colocation);
 284             return 0;
 285         }
 286 
 287         if (colocation->dependent_role == pcmk_role_promoted) {
 288             // We're choosing a role for the dependent
 289             return pcmk__update_promotable_dependent_priority(primary,
 290                                                               dependent,
 291                                                               colocation);
 292         }
 293     }
 294 
 295     // Apply interleaved colocations
 296     if (can_interleave(colocation)) {
 297         const pcmk_resource_t *primary_instance = NULL;
 298 
 299         primary_instance = pcmk__find_compatible_instance(dependent, primary,
 300                                                           pcmk_role_unknown,
 301                                                           false);
 302         if (primary_instance != NULL) {
 303             pcmk__rsc_debug(primary, "Interleaving %s with %s",
 304                             dependent->id, primary_instance->id);
 305 
 306             return dependent->cmds->apply_coloc_score(dependent,
 307                                                       primary_instance,
 308                                                       colocation, true);
 309         }
 310 
 311         if (colocation->score >= PCMK_SCORE_INFINITY) {
 312             crm_notice("%s cannot run because it cannot interleave with "
 313                        "any instance of %s", dependent->id, primary->id);
 314             pcmk__assign_resource(dependent, NULL, true, true);
 315 
 316         } else {
 317             pcmk__rsc_debug(primary,
 318                             "%s will not colocate with %s "
 319                             "because no instance can interleave with it",
 320                             dependent->id, primary->id);
 321         }
 322 
 323         return 0;
 324     }
 325 
 326     // Apply mandatory colocations
 327     if (colocation->score >= PCMK_SCORE_INFINITY) {
 328         GList *primary_nodes = NULL;
 329 
 330         // Dependent can run only where primary will have unblocked instances
 331         for (iter = primary->children; iter != NULL; iter = iter->next) {
 332             const pcmk_resource_t *instance = iter->data;
 333             pcmk_node_t *chosen = instance->fns->location(instance, NULL, 0);
 334 
 335             if ((chosen != NULL)
 336                 && !is_set_recursive(instance, pcmk_rsc_blocked, TRUE)) {
 337                 pcmk__rsc_trace(primary, "Allowing %s: %s %d",
 338                                 colocation->id, pcmk__node_name(chosen),
 339                                 chosen->weight);
 340                 primary_nodes = g_list_prepend(primary_nodes, chosen);
 341             }
 342         }
 343         pcmk__colocation_intersect_nodes(dependent, primary, colocation,
 344                                          primary_nodes, false);
 345         g_list_free(primary_nodes);
 346         return 0;
 347     }
 348 
 349     // Apply optional colocations
 350     for (iter = primary->children; iter != NULL; iter = iter->next) {
 351         const pcmk_resource_t *instance = iter->data;
 352         int instance_delta = instance->cmds->apply_coloc_score(dependent,
 353                                                                instance,
 354                                                                colocation,
 355                                                                false);
 356 
 357         priority_delta = pcmk__add_scores(priority_delta, instance_delta);
 358     }
 359     return priority_delta;
 360 }
 361 
 362 // Clone implementation of pcmk_assignment_methods_t:with_this_colocations()
 363 void
 364 pcmk__with_clone_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 365                              const pcmk_resource_t *orig_rsc, GList **list)
 366 {
 367     CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
 368 
 369     pcmk__add_with_this_list(list, rsc->rsc_cons_lhs, orig_rsc);
 370 
 371     if (rsc->parent != NULL) {
 372         rsc->parent->cmds->with_this_colocations(rsc->parent, orig_rsc, list);
 373     }
 374 }
 375 
 376 // Clone implementation of pcmk_assignment_methods_t:this_with_colocations()
 377 void
 378 pcmk__clone_with_colocations(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 379                              const pcmk_resource_t *orig_rsc, GList **list)
 380 {
 381     CRM_CHECK((rsc != NULL) && (orig_rsc != NULL) && (list != NULL), return);
 382 
 383     pcmk__add_this_with_list(list, rsc->rsc_cons, orig_rsc);
 384 
 385     if (rsc->parent != NULL) {
 386         rsc->parent->cmds->this_with_colocations(rsc->parent, orig_rsc, list);
 387     }
 388 }
 389 
 390 /*!
 391  * \internal
 392  * \brief Return action flags for a given clone resource action
 393  *
 394  * \param[in,out] action  Action to get flags for
 395  * \param[in]     node    If not NULL, limit effects to this node
 396  *
 397  * \return Flags appropriate to \p action on \p node
 398  */
 399 uint32_t
 400 pcmk__clone_action_flags(pcmk_action_t *action, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 401 {
 402     CRM_ASSERT((action != NULL) && pcmk__is_clone(action->rsc));
 403 
 404     return pcmk__collective_action_flags(action, action->rsc->children, node);
 405 }
 406 
 407 /*!
 408  * \internal
 409  * \brief Apply a location constraint to a clone resource's allowed node scores
 410  *
 411  * \param[in,out] rsc       Clone resource to apply constraint to
 412  * \param[in,out] location  Location constraint to apply
 413  */
 414 void
 415 pcmk__clone_apply_location(pcmk_resource_t *rsc, pcmk__location_t *location)
     /* [previous][next][first][last][top][bottom][index][help] */
 416 {
 417     CRM_CHECK((location != NULL) && pcmk__is_clone(rsc), return);
 418 
 419     pcmk__apply_location(rsc, location);
 420 
 421     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 422         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
 423 
 424         instance->cmds->apply_location(instance, location);
 425     }
 426 }
 427 
 428 // GFunc wrapper for calling the action_flags() resource method
 429 static void
 430 call_action_flags(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 431 {
 432     pcmk_resource_t *rsc = user_data;
 433 
 434     rsc->cmds->action_flags((pcmk_action_t *) data, NULL);
 435 }
 436 
 437 /*!
 438  * \internal
 439  * \brief Add a clone resource's actions to the transition graph
 440  *
 441  * \param[in,out] rsc  Resource whose actions should be added
 442  */
 443 void
 444 pcmk__clone_add_actions_to_graph(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 445 {
 446     CRM_ASSERT(pcmk__is_clone(rsc));
 447 
 448     g_list_foreach(rsc->actions, call_action_flags, rsc);
 449     pe__create_clone_notifications(rsc);
 450 
 451     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 452         pcmk_resource_t *child_rsc = (pcmk_resource_t *) iter->data;
 453 
 454         child_rsc->cmds->add_actions_to_graph(child_rsc);
 455     }
 456 
 457     pcmk__add_rsc_actions_to_graph(rsc);
 458     pe__free_clone_notification_data(rsc);
 459 }
 460 
 461 /*!
 462  * \internal
 463  * \brief Check whether a resource or any children have been probed on a node
 464  *
 465  * \param[in] rsc   Resource to check
 466  * \param[in] node  Node to check
 467  *
 468  * \return true if \p node is in the known_on table of \p rsc or any of its
 469  *         children, otherwise false
 470  */
 471 static bool
 472 rsc_probed_on(const pcmk_resource_t *rsc, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 473 {
 474     if (rsc->children != NULL) {
 475         for (GList *child_iter = rsc->children; child_iter != NULL;
 476              child_iter = child_iter->next) {
 477 
 478             pcmk_resource_t *child = (pcmk_resource_t *) child_iter->data;
 479 
 480             if (rsc_probed_on(child, node)) {
 481                 return true;
 482             }
 483         }
 484         return false;
 485     }
 486 
 487     if (rsc->known_on != NULL) {
 488         GHashTableIter iter;
 489         pcmk_node_t *known_node = NULL;
 490 
 491         g_hash_table_iter_init(&iter, rsc->known_on);
 492         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &known_node)) {
 493             if (pcmk__same_node(node, known_node)) {
 494                 return true;
 495             }
 496         }
 497     }
 498     return false;
 499 }
 500 
 501 /*!
 502  * \internal
 503  * \brief Find clone instance that has been probed on given node
 504  *
 505  * \param[in] clone  Clone resource to check
 506  * \param[in] node   Node to check
 507  *
 508  * \return Instance of \p clone that has been probed on \p node if any,
 509  *         otherwise NULL
 510  */
 511 static pcmk_resource_t *
 512 find_probed_instance_on(const pcmk_resource_t *clone, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 513 {
 514     for (GList *iter = clone->children; iter != NULL; iter = iter->next) {
 515         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
 516 
 517         if (rsc_probed_on(instance, node)) {
 518             return instance;
 519         }
 520     }
 521     return NULL;
 522 }
 523 
 524 /*!
 525  * \internal
 526  * \brief Probe an anonymous clone on a node
 527  *
 528  * \param[in,out] clone  Anonymous clone to probe
 529  * \param[in,out] node   Node to probe \p clone on
 530  */
 531 static bool
 532 probe_anonymous_clone(pcmk_resource_t *clone, pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 533 {
 534     // Check whether we already probed an instance on this node
 535     pcmk_resource_t *child = find_probed_instance_on(clone, node);
 536 
 537     // Otherwise, check if we plan to start an instance on this node
 538     for (GList *iter = clone->children; (iter != NULL) && (child == NULL);
 539          iter = iter->next) {
 540         pcmk_resource_t *instance = (pcmk_resource_t *) iter->data;
 541         const pcmk_node_t *instance_node = NULL;
 542 
 543         instance_node = instance->fns->location(instance, NULL, 0);
 544         if (pcmk__same_node(instance_node, node)) {
 545             child = instance;
 546         }
 547     }
 548 
 549     // Otherwise, use the first clone instance
 550     if (child == NULL) {
 551         child = clone->children->data;
 552     }
 553 
 554     // Anonymous clones only need to probe a single instance
 555     return child->cmds->create_probe(child, node);
 556 }
 557 
 558 /*!
 559  * \internal
 560  * \brief Schedule any probes needed for a resource on a node
 561  *
 562  * \param[in,out] rsc   Resource to create probe for
 563  * \param[in,out] node  Node to create probe on
 564  *
 565  * \return true if any probe was created, otherwise false
 566  */
 567 bool
 568 pcmk__clone_create_probe(pcmk_resource_t *rsc, pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     CRM_ASSERT((node != NULL) && pcmk__is_clone(rsc));
 571 
 572     if (rsc->exclusive_discover) {
 573         /* The clone is configured to be probed only where a location constraint
 574          * exists with PCMK_XA_RESOURCE_DISCOVERY set to exclusive.
 575          *
 576          * This check is not strictly necessary here since the instance's
 577          * create_probe() method would also check, but doing it here is more
 578          * efficient (especially for unique clones with a large number of
 579          * instances), and affects the CRM_meta_notify_available_uname variable
 580          * passed with notify actions.
 581          */
 582         pcmk_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes,
 583                                                    node->details->id);
 584 
 585         if ((allowed == NULL)
 586             || (allowed->rsc_discover_mode != pcmk_probe_exclusive)) {
 587             /* This node is not marked for resource discovery. Remove it from
 588              * allowed_nodes so that notifications contain only nodes that the
 589              * clone can possibly run on.
 590              */
 591             pcmk__rsc_trace(rsc,
 592                             "Skipping probe for %s on %s because resource has "
 593                             "exclusive discovery but is not allowed on node",
 594                             rsc->id, pcmk__node_name(node));
 595             g_hash_table_remove(rsc->allowed_nodes, node->details->id);
 596             return false;
 597         }
 598     }
 599 
 600     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
 601     if (pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
 602         return pcmk__probe_resource_list(rsc->children, node);
 603     } else {
 604         return probe_anonymous_clone(rsc, node);
 605     }
 606 }
 607 
 608 /*!
 609  * \internal
 610  * \brief Add meta-attributes relevant to transition graph actions to XML
 611  *
 612  * Add clone-specific meta-attributes needed for transition graph actions.
 613  *
 614  * \param[in]     rsc  Clone resource whose meta-attributes should be added
 615  * \param[in,out] xml  Transition graph action attributes XML to add to
 616  */
 617 void
 618 pcmk__clone_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 619 {
 620     char *name = NULL;
 621 
 622     CRM_ASSERT(pcmk__is_clone(rsc) && (xml != NULL));
 623 
 624     name = crm_meta_name(PCMK_META_GLOBALLY_UNIQUE);
 625     crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_unique));
 626     free(name);
 627 
 628     name = crm_meta_name(PCMK_META_NOTIFY);
 629     crm_xml_add(xml, name, pcmk__flag_text(rsc->flags, pcmk_rsc_notify));
 630     free(name);
 631 
 632     name = crm_meta_name(PCMK_META_CLONE_MAX);
 633     crm_xml_add_int(xml, name, pe__clone_max(rsc));
 634     free(name);
 635 
 636     name = crm_meta_name(PCMK_META_CLONE_NODE_MAX);
 637     crm_xml_add_int(xml, name, pe__clone_node_max(rsc));
 638     free(name);
 639 
 640     if (pcmk_is_set(rsc->flags, pcmk_rsc_promotable)) {
 641         int promoted_max = pe__clone_promoted_max(rsc);
 642         int promoted_node_max = pe__clone_promoted_node_max(rsc);
 643 
 644         name = crm_meta_name(PCMK_META_PROMOTED_MAX);
 645         crm_xml_add_int(xml, name, promoted_max);
 646         free(name);
 647 
 648         name = crm_meta_name(PCMK_META_PROMOTED_NODE_MAX);
 649         crm_xml_add_int(xml, name, promoted_node_max);
 650         free(name);
 651 
 652         /* @COMPAT Maintain backward compatibility with resource agents that
 653          * expect the old names (deprecated since 2.0.0).
 654          */
 655         name = crm_meta_name(PCMK__META_PROMOTED_MAX_LEGACY);
 656         crm_xml_add_int(xml, name, promoted_max);
 657         free(name);
 658 
 659         name = crm_meta_name(PCMK__META_PROMOTED_NODE_MAX_LEGACY);
 660         crm_xml_add_int(xml, name, promoted_node_max);
 661         free(name);
 662     }
 663 }
 664 
 665 // Clone implementation of pcmk_assignment_methods_t:add_utilization()
 666 void
 667 pcmk__clone_add_utilization(const pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 668                             const pcmk_resource_t *orig_rsc, GList *all_rscs,
 669                             GHashTable *utilization)
 670 {
 671     bool existing = false;
 672     pcmk_resource_t *child = NULL;
 673 
 674     CRM_ASSERT(pcmk__is_clone(rsc) && (orig_rsc != NULL)
 675                && (utilization != NULL));
 676 
 677     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unassigned)) {
 678         return;
 679     }
 680 
 681     // Look for any child already existing in the list
 682     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 683         child = (pcmk_resource_t *) iter->data;
 684         if (g_list_find(all_rscs, child)) {
 685             existing = true; // Keep checking remaining children
 686         } else {
 687             // If this is a clone of a group, look for group's members
 688             for (GList *member_iter = child->children; member_iter != NULL;
 689                  member_iter = member_iter->next) {
 690 
 691                 pcmk_resource_t *member = (pcmk_resource_t *) member_iter->data;
 692 
 693                 if (g_list_find(all_rscs, member) != NULL) {
 694                     // Add *child's* utilization, not group member's
 695                     child->cmds->add_utilization(child, orig_rsc, all_rscs,
 696                                                  utilization);
 697                     existing = true;
 698                     break;
 699                 }
 700             }
 701         }
 702     }
 703 
 704     if (!existing && (rsc->children != NULL)) {
 705         // If nothing was found, still add first child's utilization
 706         child = (pcmk_resource_t *) rsc->children->data;
 707 
 708         child->cmds->add_utilization(child, orig_rsc, all_rscs, utilization);
 709     }
 710 }
 711 
 712 // Clone implementation of pcmk_assignment_methods_t:shutdown_lock()
 713 void
 714 pcmk__clone_shutdown_lock(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 715 {
 716     CRM_ASSERT(pcmk__is_clone(rsc));
 717     return; // Clones currently don't support shutdown locks
 718 }

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