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

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