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

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