root/lib/pacemaker/pcmk_sched_clone.c

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

DEFINITIONS

This source file includes following definitions.
  1. can_run_instance
  2. allocate_instance
  3. append_parent_colocation
  4. distribute_children
  5. pcmk__clone_allocate
  6. clone_update_pseudo_status
  7. find_rsc_action
  8. child_ordering_constraints
  9. clone_create_actions
  10. clone_create_pseudo_actions
  11. clone_internal_constraints
  12. is_child_compatible
  13. find_compatible_child
  14. pcmk__clone_apply_coloc_score
  15. clone_child_action
  16. summary_action_flags
  17. clone_action_flags
  18. clone_rsc_location
  19. clone_expand
  20. rsc_known_on
  21. find_instance_on
  22. probe_anonymous_clone
  23. clone_create_probe
  24. clone_append_meta
  25. pcmk__clone_add_utilization
  26. pcmk__clone_shutdown_lock

   1 /*
   2  * Copyright 2004-2022 the Pacemaker project contributors
   3  *
   4  * The version control history for this file may have further details.
   5  *
   6  * This source code is licensed under the GNU General Public License version 2
   7  * or later (GPLv2+) WITHOUT ANY WARRANTY.
   8  */
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <crm/msg_xml.h>
  13 #include <pacemaker-internal.h>
  14 
  15 #include "libpacemaker_private.h"
  16 
  17 #define VARIANT_CLONE 1
  18 #include <lib/pengine/variant.h>
  19 
  20 static void append_parent_colocation(pe_resource_t * rsc, pe_resource_t * child, gboolean all);
  21 
  22 static pe_node_t *
  23 can_run_instance(pe_resource_t * rsc, pe_node_t * node, int limit)
     /* [previous][next][first][last][top][bottom][index][help] */
  24 {
  25     pe_node_t *local_node = NULL;
  26 
  27     if (node == NULL && rsc->allowed_nodes) {
  28         GHashTableIter iter;
  29         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
  30         while (g_hash_table_iter_next(&iter, NULL, (void **)&local_node)) {
  31             can_run_instance(rsc, local_node, limit);
  32         }
  33         return NULL;
  34     }
  35 
  36     if (!node) {
  37         /* make clang analyzer happy */
  38         goto bail;
  39 
  40     } else if (!pcmk__node_available(node, false, false)) {
  41         goto bail;
  42 
  43     } else if (pcmk_is_set(rsc->flags, pe_rsc_orphan)) {
  44         goto bail;
  45     }
  46 
  47     local_node = pcmk__top_allowed_node(rsc, node);
  48 
  49     if (local_node == NULL) {
  50         crm_warn("%s cannot run on %s: node not allowed",
  51                  rsc->id, pe__node_name(node));
  52         goto bail;
  53 
  54     } else if (local_node->weight < 0) {
  55         common_update_score(rsc, node->details->id, local_node->weight);
  56         pe_rsc_trace(rsc, "%s cannot run on %s: Parent node weight doesn't allow it.",
  57                      rsc->id, pe__node_name(node));
  58 
  59     } else if (local_node->count < limit) {
  60         pe_rsc_trace(rsc, "%s can run on %s (already running %d)",
  61                      rsc->id, pe__node_name(node), local_node->count);
  62         return local_node;
  63 
  64     } else {
  65         pe_rsc_trace(rsc, "%s cannot run on %s: node full (%d >= %d)",
  66                      rsc->id, pe__node_name(node), local_node->count, limit);
  67     }
  68 
  69   bail:
  70     if (node) {
  71         common_update_score(rsc, node->details->id, -INFINITY);
  72     }
  73     return NULL;
  74 }
  75 
  76 static pe_node_t *
  77 allocate_instance(pe_resource_t *rsc, pe_node_t *prefer, gboolean all_coloc,
     /* [previous][next][first][last][top][bottom][index][help] */
  78                   int limit, pe_working_set_t *data_set)
  79 {
  80     pe_node_t *chosen = NULL;
  81     GHashTable *backup = NULL;
  82 
  83     CRM_ASSERT(rsc);
  84     pe_rsc_trace(rsc, "Checking allocation of %s (preferring %s, using %s parent colocations)",
  85                  rsc->id, (prefer? prefer->details->uname: "none"),
  86                  (all_coloc? "all" : "some"));
  87 
  88     if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
  89         return rsc->fns->location(rsc, NULL, FALSE);
  90 
  91     } else if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
  92         pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
  93         return NULL;
  94     }
  95 
  96     /* Only include positive colocation preferences of dependent resources
  97      * if not every node will get a copy of the clone
  98      */
  99     append_parent_colocation(rsc->parent, rsc, all_coloc);
 100 
 101     if (prefer) {
 102         pe_node_t *local_prefer = g_hash_table_lookup(rsc->allowed_nodes, prefer->details->id);
 103 
 104         if (local_prefer == NULL || local_prefer->weight < 0) {
 105             pe_rsc_trace(rsc, "Not pre-allocating %s to %s - unavailable", rsc->id,
 106                          pe__node_name(prefer));
 107             return NULL;
 108         }
 109     }
 110 
 111     can_run_instance(rsc, NULL, limit);
 112 
 113     backup = pcmk__copy_node_table(rsc->allowed_nodes);
 114     pe_rsc_trace(rsc, "Allocating instance %s", rsc->id);
 115     chosen = rsc->cmds->assign(rsc, prefer);
 116     if (chosen && prefer && (chosen->details != prefer->details)) {
 117         crm_info("Not pre-allocating %s to %s because %s is better",
 118                  rsc->id, pe__node_name(prefer), pe__node_name(chosen));
 119         g_hash_table_destroy(rsc->allowed_nodes);
 120         rsc->allowed_nodes = backup;
 121         pcmk__unassign_resource(rsc);
 122         chosen = NULL;
 123         backup = NULL;
 124     }
 125     if (chosen) {
 126         pe_node_t *local_node = pcmk__top_allowed_node(rsc, chosen);
 127 
 128         if (local_node) {
 129             local_node->count++;
 130 
 131         } else if (pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 132             /* what to do? we can't enforce per-node limits in this case */
 133             pcmk__config_err("%s not found in %s (list of %d)",
 134                              chosen->details->id, rsc->parent->id,
 135                              g_hash_table_size(rsc->parent->allowed_nodes));
 136         }
 137     }
 138 
 139     if(backup) {
 140         g_hash_table_destroy(backup);
 141     }
 142     return chosen;
 143 }
 144 
 145 static void
 146 append_parent_colocation(pe_resource_t * rsc, pe_resource_t * child, gboolean all)
     /* [previous][next][first][last][top][bottom][index][help] */
 147 {
 148 
 149     GList *gIter = NULL;
 150 
 151     gIter = rsc->rsc_cons;
 152     for (; gIter != NULL; gIter = gIter->next) {
 153         pcmk__colocation_t *cons = (pcmk__colocation_t *) gIter->data;
 154 
 155         if (all || cons->score < 0 || cons->score == INFINITY) {
 156             pcmk__add_this_with(child, cons);
 157         }
 158     }
 159 
 160     gIter = rsc->rsc_cons_lhs;
 161     for (; gIter != NULL; gIter = gIter->next) {
 162         pcmk__colocation_t *cons = (pcmk__colocation_t *) gIter->data;
 163 
 164         if (!pcmk__colocation_has_influence(cons, child)) {
 165            continue;
 166         }
 167         if (all || cons->score < 0) {
 168             pcmk__add_with_this(child, cons);
 169         }
 170     }
 171 }
 172 
 173 
 174 void
 175 distribute_children(pe_resource_t *rsc, GList *children, GList *nodes,
 176                     int max, int per_host_max, pe_working_set_t * data_set);
 177 
 178 void
 179 distribute_children(pe_resource_t *rsc, GList *children, GList *nodes,
     /* [previous][next][first][last][top][bottom][index][help] */
 180                     int max, int per_host_max, pe_working_set_t * data_set) 
 181 {
 182     int loop_max = 0;
 183     int allocated = 0;
 184     int available_nodes = 0;
 185     bool all_coloc = false;
 186 
 187     /* count now tracks the number of clones currently allocated */
 188     for(GList *nIter = nodes; nIter != NULL; nIter = nIter->next) {
 189         pe_node_t *node = nIter->data;
 190 
 191         node->count = 0;
 192         if (pcmk__node_available(node, false, false)) {
 193             available_nodes++;
 194         }
 195     }
 196 
 197     all_coloc = (max < available_nodes) ? true : false;
 198 
 199     if(available_nodes) {
 200         loop_max = max / available_nodes;
 201     }
 202     if (loop_max < 1) {
 203         loop_max = 1;
 204     }
 205 
 206     pe_rsc_debug(rsc, "Allocating up to %d %s instances to a possible %d nodes (at most %d per host, %d optimal)",
 207                  max, rsc->id, available_nodes, per_host_max, loop_max);
 208 
 209     /* Pre-allocate as many instances as we can to their current location */
 210     for (GList *gIter = children; gIter != NULL && allocated < max; gIter = gIter->next) {
 211         pe_resource_t *child = (pe_resource_t *) gIter->data;
 212         pe_node_t *child_node = NULL;
 213         pe_node_t *local_node = NULL;
 214 
 215         if ((child->running_on == NULL)
 216             || !pcmk_is_set(child->flags, pe_rsc_provisional)
 217             || pcmk_is_set(child->flags, pe_rsc_failed)) {
 218 
 219             continue;
 220         }
 221 
 222         child_node = pe__current_node(child);
 223         local_node = pcmk__top_allowed_node(child, child_node);
 224 
 225         pe_rsc_trace(rsc,
 226                      "Checking pre-allocation of %s to %s (%d remaining of %d)",
 227                      child->id, pe__node_name(child_node), max - allocated,
 228                      max);
 229 
 230         if (!pcmk__node_available(child_node, true, false)) {
 231             pe_rsc_trace(rsc, "Not pre-allocating because %s can not run %s",
 232                          pe__node_name(child_node), child->id);
 233             continue;
 234         }
 235 
 236         if ((local_node != NULL) && (local_node->count >= loop_max)) {
 237             pe_rsc_trace(rsc,
 238                          "Not pre-allocating because %s already allocated "
 239                          "optimal instances", pe__node_name(child_node));
 240             continue;
 241         }
 242 
 243         if (allocate_instance(child, child_node, all_coloc, per_host_max,
 244                               data_set)) {
 245             pe_rsc_trace(rsc, "Pre-allocated %s to %s", child->id,
 246                          pe__node_name(child_node));
 247             allocated++;
 248         }
 249     }
 250 
 251     pe_rsc_trace(rsc, "Done pre-allocating (%d of %d)", allocated, max);
 252 
 253     for (GList *gIter = children; gIter != NULL; gIter = gIter->next) {
 254         pe_resource_t *child = (pe_resource_t *) gIter->data;
 255 
 256         if (child->running_on != NULL) {
 257             pe_node_t *child_node = pe__current_node(child);
 258             pe_node_t *local_node = pcmk__top_allowed_node(child, child_node);
 259 
 260             if (local_node == NULL) {
 261                 crm_err("%s is running on %s which isn't allowed",
 262                         child->id, pe__node_name(child_node));
 263             }
 264         }
 265 
 266         if (!pcmk_is_set(child->flags, pe_rsc_provisional)) {
 267         } else if (allocated >= max) {
 268             pe_rsc_debug(rsc, "Child %s not allocated - limit reached %d %d", child->id, allocated, max);
 269             resource_location(child, NULL, -INFINITY, "clone:limit_reached", data_set);
 270         } else {
 271             if (allocate_instance(child, NULL, all_coloc, per_host_max,
 272                                   data_set)) {
 273                 allocated++;
 274             }
 275         }
 276     }
 277 
 278     pe_rsc_debug(rsc, "Allocated %d %s instances of a possible %d",
 279                  allocated, rsc->id, max);
 280 }
 281 
 282 /*!
 283  * \internal
 284  * \brief Assign a clone resource to a node
 285  *
 286  * \param[in,out] rsc     Resource to assign to a node
 287  * \param[in]     prefer  Node to prefer, if all else is equal
 288  *
 289  * \return Node that \p rsc is assigned to, if assigned entirely to one node
 290  */
 291 pe_node_t *
 292 pcmk__clone_allocate(pe_resource_t *rsc, const pe_node_t *prefer)
     /* [previous][next][first][last][top][bottom][index][help] */
 293 {
 294     GList *nodes = NULL;
 295     clone_variant_data_t *clone_data = NULL;
 296 
 297     get_clone_variant_data(clone_data, rsc);
 298 
 299     if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
 300         return NULL;
 301 
 302     } else if (pcmk_is_set(rsc->flags, pe_rsc_allocating)) {
 303         pe_rsc_debug(rsc, "Dependency loop detected involving %s", rsc->id);
 304         return NULL;
 305     }
 306 
 307     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 308         pcmk__add_promotion_scores(rsc);
 309     }
 310 
 311     pe__set_resource_flags(rsc, pe_rsc_allocating);
 312 
 313     /* This information is used by pcmk__cmp_instance() when deciding the order
 314      * in which to assign clone instances to nodes.
 315      */
 316     for (GList *gIter = rsc->rsc_cons; gIter != NULL; gIter = gIter->next) {
 317         pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
 318 
 319         pe_rsc_trace(rsc, "%s: Allocating %s first",
 320                      rsc->id, constraint->primary->id);
 321         constraint->primary->cmds->assign(constraint->primary, prefer);
 322     }
 323 
 324     for (GList *gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
 325         pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
 326 
 327         if (pcmk__colocation_has_influence(constraint, NULL)) {
 328             pe_resource_t *dependent = constraint->dependent;
 329             const char *attr = constraint->node_attribute;
 330             const float factor = constraint->score / (float) INFINITY;
 331             const uint32_t flags = pcmk__coloc_select_active
 332                                    |pcmk__coloc_select_nonnegative;
 333 
 334             pcmk__add_colocated_node_scores(dependent, rsc->id,
 335                                             &rsc->allowed_nodes, attr, factor,
 336                                             flags);
 337         }
 338     }
 339 
 340     pe__show_node_weights(!pcmk_is_set(rsc->cluster->flags, pe_flag_show_scores),
 341                           rsc, __func__, rsc->allowed_nodes, rsc->cluster);
 342 
 343     nodes = g_hash_table_get_values(rsc->allowed_nodes);
 344     nodes = pcmk__sort_nodes(nodes, NULL);
 345     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance);
 346     distribute_children(rsc, rsc->children, nodes, clone_data->clone_max,
 347                         clone_data->clone_node_max, rsc->cluster);
 348     g_list_free(nodes);
 349 
 350     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 351         pcmk__set_instance_roles(rsc);
 352     }
 353 
 354     pe__clear_resource_flags(rsc, pe_rsc_provisional|pe_rsc_allocating);
 355     pe_rsc_trace(rsc, "Done allocating %s", rsc->id);
 356     return NULL;
 357 }
 358 
 359 static void
 360 clone_update_pseudo_status(pe_resource_t * rsc, gboolean * stopping, gboolean * starting,
     /* [previous][next][first][last][top][bottom][index][help] */
 361                            gboolean * active)
 362 {
 363     GList *gIter = NULL;
 364 
 365     if (rsc->children) {
 366 
 367         gIter = rsc->children;
 368         for (; gIter != NULL; gIter = gIter->next) {
 369             pe_resource_t *child = (pe_resource_t *) gIter->data;
 370 
 371             clone_update_pseudo_status(child, stopping, starting, active);
 372         }
 373 
 374         return;
 375     }
 376 
 377     CRM_ASSERT(active != NULL);
 378     CRM_ASSERT(starting != NULL);
 379     CRM_ASSERT(stopping != NULL);
 380 
 381     if (rsc->running_on) {
 382         *active = TRUE;
 383     }
 384 
 385     gIter = rsc->actions;
 386     for (; gIter != NULL; gIter = gIter->next) {
 387         pe_action_t *action = (pe_action_t *) gIter->data;
 388 
 389         if (*starting && *stopping) {
 390             return;
 391 
 392         } else if (pcmk_is_set(action->flags, pe_action_optional)) {
 393             pe_rsc_trace(rsc, "Skipping optional: %s", action->uuid);
 394             continue;
 395 
 396         } else if (!pcmk_any_flags_set(action->flags,
 397                                        pe_action_pseudo|pe_action_runnable)) {
 398             pe_rsc_trace(rsc, "Skipping unrunnable: %s", action->uuid);
 399             continue;
 400 
 401         } else if (pcmk__str_eq(RSC_STOP, action->task, pcmk__str_casei)) {
 402             pe_rsc_trace(rsc, "Stopping due to: %s", action->uuid);
 403             *stopping = TRUE;
 404 
 405         } else if (pcmk__str_eq(RSC_START, action->task, pcmk__str_casei)) {
 406             if (!pcmk_is_set(action->flags, pe_action_runnable)) {
 407                 pe_rsc_trace(rsc, "Skipping pseudo-op: %s run=%d, pseudo=%d",
 408                              action->uuid,
 409                              pcmk_is_set(action->flags, pe_action_runnable),
 410                              pcmk_is_set(action->flags, pe_action_pseudo));
 411             } else {
 412                 pe_rsc_trace(rsc, "Starting due to: %s", action->uuid);
 413                 pe_rsc_trace(rsc, "%s run=%d, pseudo=%d",
 414                              action->uuid,
 415                              pcmk_is_set(action->flags, pe_action_runnable),
 416                              pcmk_is_set(action->flags, pe_action_pseudo));
 417                 *starting = TRUE;
 418             }
 419         }
 420     }
 421 }
 422 
 423 static pe_action_t *
 424 find_rsc_action(pe_resource_t *rsc, const char *task)
     /* [previous][next][first][last][top][bottom][index][help] */
 425 {
 426     pe_action_t *match = NULL;
 427     GList *actions = pe__resource_actions(rsc, NULL, task, FALSE);
 428 
 429     for (GList *item = actions; item != NULL; item = item->next) {
 430         pe_action_t *op = (pe_action_t *) item->data;
 431 
 432         if (!pcmk_is_set(op->flags, pe_action_optional)) {
 433             if (match != NULL) {
 434                 // More than one match, don't return any
 435                 match = NULL;
 436                 break;
 437             }
 438             match = op;
 439         }
 440     }
 441     g_list_free(actions);
 442     return match;
 443 }
 444 
 445 static void
 446 child_ordering_constraints(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 447 {
 448     pe_action_t *stop = NULL;
 449     pe_action_t *start = NULL;
 450     pe_action_t *last_stop = NULL;
 451     pe_action_t *last_start = NULL;
 452     GList *gIter = NULL;
 453 
 454     if (!pe__clone_is_ordered(rsc)) {
 455         return;
 456     }
 457 
 458     /* we have to maintain a consistent sorted child list when building order constraints */
 459     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
 460 
 461     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 462         pe_resource_t *child = (pe_resource_t *) gIter->data;
 463 
 464         stop = find_rsc_action(child, RSC_STOP);
 465         if (stop) {
 466             if (last_stop) {
 467                 /* child/child relative stop */
 468                 order_actions(stop, last_stop, pe_order_optional);
 469             }
 470             last_stop = stop;
 471         }
 472 
 473         start = find_rsc_action(child, RSC_START);
 474         if (start) {
 475             if (last_start) {
 476                 /* child/child relative start */
 477                 order_actions(last_start, start, pe_order_optional);
 478             }
 479             last_start = start;
 480         }
 481     }
 482 }
 483 
 484 void
 485 clone_create_actions(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 486 {
 487     clone_variant_data_t *clone_data = NULL;
 488 
 489     get_clone_variant_data(clone_data, rsc);
 490 
 491     pe_rsc_debug(rsc, "Creating actions for clone %s", rsc->id);
 492     clone_create_pseudo_actions(rsc, rsc->children, &clone_data->start_notify,
 493                                 &clone_data->stop_notify);
 494     child_ordering_constraints(rsc, rsc->cluster);
 495 
 496     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 497         pcmk__create_promotable_actions(rsc);
 498     }
 499 }
 500 
 501 void
 502 clone_create_pseudo_actions(pe_resource_t *rsc, GList *children,
     /* [previous][next][first][last][top][bottom][index][help] */
 503                             notify_data_t **start_notify,
 504                             notify_data_t **stop_notify)
 505 {
 506     gboolean child_active = FALSE;
 507     gboolean child_starting = FALSE;
 508     gboolean child_stopping = FALSE;
 509     gboolean allow_dependent_migrations = TRUE;
 510 
 511     pe_action_t *stop = NULL;
 512     pe_action_t *stopped = NULL;
 513 
 514     pe_action_t *start = NULL;
 515     pe_action_t *started = NULL;
 516 
 517     pe_rsc_trace(rsc, "Creating actions for %s", rsc->id);
 518 
 519     for (GList *gIter = children; gIter != NULL; gIter = gIter->next) {
 520         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 521         gboolean starting = FALSE;
 522         gboolean stopping = FALSE;
 523 
 524         child_rsc->cmds->create_actions(child_rsc);
 525         clone_update_pseudo_status(child_rsc, &stopping, &starting, &child_active);
 526         if (stopping && starting) {
 527             allow_dependent_migrations = FALSE;
 528         }
 529 
 530         child_stopping |= stopping;
 531         child_starting |= starting;
 532     }
 533 
 534     /* start */
 535     start = pe__new_rsc_pseudo_action(rsc, RSC_START, !child_starting, true);
 536     started = pe__new_rsc_pseudo_action(rsc, RSC_STARTED, !child_starting,
 537                                         false);
 538     started->priority = INFINITY;
 539 
 540     if (child_active || child_starting) {
 541         pe__set_action_flags(started, pe_action_runnable);
 542     }
 543 
 544     if (start_notify != NULL && *start_notify == NULL) {
 545         *start_notify = pe__clone_notif_pseudo_ops(rsc, RSC_START, start,
 546                                                    started);
 547     }
 548 
 549     /* stop */
 550     stop = pe__new_rsc_pseudo_action(rsc, RSC_STOP, !child_stopping, true);
 551     stopped = pe__new_rsc_pseudo_action(rsc, RSC_STOPPED, !child_stopping,
 552                                         true);
 553     stopped->priority = INFINITY;
 554     if (allow_dependent_migrations) {
 555         pe__set_action_flags(stop, pe_action_migrate_runnable);
 556     }
 557 
 558     if (stop_notify != NULL && *stop_notify == NULL) {
 559         *stop_notify = pe__clone_notif_pseudo_ops(rsc, RSC_STOP, stop, stopped);
 560 
 561         if (start_notify && *start_notify && *stop_notify) {
 562             order_actions((*stop_notify)->post_done, (*start_notify)->pre, pe_order_optional);
 563         }
 564     }
 565 }
 566 
 567 void
 568 clone_internal_constraints(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 569 {
 570     pe_resource_t *last_rsc = NULL;
 571     GList *gIter;
 572     bool ordered = pe__clone_is_ordered(rsc);
 573 
 574     pe_rsc_trace(rsc, "Internal constraints for %s", rsc->id);
 575     pcmk__order_resource_actions(rsc, RSC_STOPPED, rsc, RSC_START,
 576                                  pe_order_optional);
 577     pcmk__order_resource_actions(rsc, RSC_START, rsc, RSC_STARTED,
 578                                  pe_order_runnable_left);
 579     pcmk__order_resource_actions(rsc, RSC_STOP, rsc, RSC_STOPPED,
 580                                  pe_order_runnable_left);
 581 
 582     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 583         pcmk__order_resource_actions(rsc, RSC_DEMOTED, rsc, RSC_STOP,
 584                                      pe_order_optional);
 585         pcmk__order_resource_actions(rsc, RSC_STARTED, rsc, RSC_PROMOTE,
 586                                      pe_order_runnable_left);
 587     }
 588 
 589     if (ordered) {
 590         /* we have to maintain a consistent sorted child list when building order constraints */
 591         rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
 592     }
 593     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 594         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 595 
 596         child_rsc->cmds->internal_constraints(child_rsc);
 597 
 598         pcmk__order_starts(rsc, child_rsc,
 599                            pe_order_runnable_left|pe_order_implies_first_printed);
 600         pcmk__order_resource_actions(child_rsc, RSC_START, rsc, RSC_STARTED,
 601                                      pe_order_implies_then_printed);
 602         if (ordered && (last_rsc != NULL)) {
 603             pcmk__order_starts(last_rsc, child_rsc, pe_order_optional);
 604         }
 605 
 606         pcmk__order_stops(rsc, child_rsc, pe_order_implies_first_printed);
 607         pcmk__order_resource_actions(child_rsc, RSC_STOP, rsc, RSC_STOPPED,
 608                                      pe_order_implies_then_printed);
 609         if (ordered && (last_rsc != NULL)) {
 610             pcmk__order_stops(child_rsc, last_rsc, pe_order_optional);
 611         }
 612 
 613         last_rsc = child_rsc;
 614     }
 615     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
 616         pcmk__order_promotable_instances(rsc);
 617     }
 618 }
 619 
 620 gboolean
 621 is_child_compatible(const pe_resource_t *child_rsc, const pe_node_t *local_node,
     /* [previous][next][first][last][top][bottom][index][help] */
 622                     enum rsc_role_e filter, gboolean current)
 623 {
 624     pe_node_t *node = NULL;
 625     enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, current);
 626 
 627     CRM_CHECK(child_rsc && local_node, return FALSE);
 628     if (is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) {
 629         /* We only want instances that haven't failed */
 630         node = child_rsc->fns->location(child_rsc, NULL, current);
 631     }
 632 
 633     if (filter != RSC_ROLE_UNKNOWN && next_role != filter) {
 634         crm_trace("Filtered %s", child_rsc->id);
 635         return FALSE;
 636     }
 637 
 638     if (node && (node->details == local_node->details)) {
 639         return TRUE;
 640 
 641     } else if (node) {
 642         crm_trace("%s - %s vs %s", child_rsc->id, pe__node_name(node),
 643                   pe__node_name(local_node));
 644 
 645     } else {
 646         crm_trace("%s - not allocated %d", child_rsc->id, current);
 647     }
 648     return FALSE;
 649 }
 650 
 651 pe_resource_t *
 652 find_compatible_child(const pe_resource_t *local_child,
     /* [previous][next][first][last][top][bottom][index][help] */
 653                       const pe_resource_t *rsc, enum rsc_role_e filter,
 654                       gboolean current)
 655 {
 656     pe_resource_t *pair = NULL;
 657     GList *gIter = NULL;
 658     GList *scratch = NULL;
 659     pe_node_t *local_node = NULL;
 660 
 661     local_node = local_child->fns->location(local_child, NULL, current);
 662     if (local_node) {
 663         return find_compatible_child_by_node(local_child, local_node, rsc, filter, current);
 664     }
 665 
 666     scratch = g_hash_table_get_values(local_child->allowed_nodes);
 667     scratch = pcmk__sort_nodes(scratch, NULL);
 668 
 669     gIter = scratch;
 670     for (; gIter != NULL; gIter = gIter->next) {
 671         pe_node_t *node = (pe_node_t *) gIter->data;
 672 
 673         pair = find_compatible_child_by_node(local_child, node, rsc, filter, current);
 674         if (pair) {
 675             goto done;
 676         }
 677     }
 678 
 679     pe_rsc_debug(rsc, "Can't pair %s with %s", local_child->id, rsc->id);
 680   done:
 681     g_list_free(scratch);
 682     return pair;
 683 }
 684 
 685 /*!
 686  * \internal
 687  * \brief Apply a colocation's score to node weights or resource priority
 688  *
 689  * Given a colocation constraint, apply its score to the dependent's
 690  * allowed node weights (if we are still placing resources) or priority (if
 691  * we are choosing promotable clone instance roles).
 692  *
 693  * \param[in,out] dependent      Dependent resource in colocation
 694  * \param[in]     primary        Primary resource in colocation
 695  * \param[in]     colocation     Colocation constraint to apply
 696  * \param[in]     for_dependent  true if called on behalf of dependent
 697  */
 698 void
 699 pcmk__clone_apply_coloc_score(pe_resource_t *dependent,
     /* [previous][next][first][last][top][bottom][index][help] */
 700                               const pe_resource_t *primary,
 701                               const pcmk__colocation_t *colocation,
 702                               bool for_dependent)
 703 {
 704     GList *gIter = NULL;
 705     gboolean do_interleave = FALSE;
 706     const char *interleave_s = NULL;
 707 
 708     /* This should never be called for the clone itself as a dependent. Instead,
 709      * we add its colocation constraints to its instances and call the
 710      * apply_coloc_score() for the instances as dependents.
 711      */
 712     CRM_ASSERT(!for_dependent);
 713 
 714     CRM_CHECK((colocation != NULL) && (dependent != NULL) && (primary != NULL),
 715               return);
 716     CRM_CHECK(dependent->variant == pe_native, return);
 717 
 718     pe_rsc_trace(primary, "Processing constraint %s: %s -> %s %d",
 719                  colocation->id, dependent->id, primary->id, colocation->score);
 720 
 721     if (pcmk_is_set(primary->flags, pe_rsc_promotable)) {
 722         if (pcmk_is_set(primary->flags, pe_rsc_provisional)) {
 723             // We haven't placed the primary yet, so we can't apply colocation
 724             pe_rsc_trace(primary, "%s is still provisional", primary->id);
 725             return;
 726 
 727         } else if (colocation->primary_role == RSC_ROLE_UNKNOWN) {
 728             // This isn't a role-specfic colocation, so handle normally
 729             pe_rsc_trace(primary, "Handling %s as a clone colocation",
 730                          colocation->id);
 731 
 732         } else if (pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
 733             // We're placing the dependent
 734             pcmk__update_dependent_with_promotable(primary, dependent,
 735                                                    colocation);
 736             return;
 737 
 738         } else if (colocation->dependent_role == RSC_ROLE_PROMOTED) {
 739             // We're choosing roles for the dependent
 740             pcmk__update_promotable_dependent_priority(primary, dependent,
 741                                                        colocation);
 742             return;
 743         }
 744     }
 745 
 746     // Only the dependent needs to be marked for interleave
 747     interleave_s = g_hash_table_lookup(colocation->dependent->meta,
 748                                        XML_RSC_ATTR_INTERLEAVE);
 749     if (crm_is_true(interleave_s)
 750         && (colocation->dependent->variant > pe_group)) {
 751         /* @TODO Do we actually care about multiple primary copies sharing a
 752          * dependent copy anymore?
 753          */
 754         if (copies_per_node(colocation->dependent) != copies_per_node(colocation->primary)) {
 755             pcmk__config_err("Cannot interleave %s and %s because they do not "
 756                              "support the same number of instances per node",
 757                              colocation->dependent->id,
 758                              colocation->primary->id);
 759 
 760         } else {
 761             do_interleave = TRUE;
 762         }
 763     }
 764 
 765     if (pcmk_is_set(primary->flags, pe_rsc_provisional)) {
 766         pe_rsc_trace(primary, "%s is still provisional", primary->id);
 767         return;
 768 
 769     } else if (do_interleave) {
 770         pe_resource_t *primary_instance = NULL;
 771 
 772         primary_instance = find_compatible_child(dependent, primary,
 773                                                  RSC_ROLE_UNKNOWN, FALSE);
 774         if (primary_instance != NULL) {
 775             pe_rsc_debug(primary, "Pairing %s with %s",
 776                          dependent->id, primary_instance->id);
 777             dependent->cmds->apply_coloc_score(dependent, primary_instance,
 778                                                colocation, true);
 779 
 780         } else if (colocation->score >= INFINITY) {
 781             crm_notice("Cannot pair %s with instance of %s",
 782                        dependent->id, primary->id);
 783             pcmk__assign_resource(dependent, NULL, true);
 784 
 785         } else {
 786             pe_rsc_debug(primary, "Cannot pair %s with instance of %s",
 787                          dependent->id, primary->id);
 788         }
 789 
 790         return;
 791 
 792     } else if (colocation->score >= INFINITY) {
 793         GList *affected_nodes = NULL;
 794 
 795         gIter = primary->children;
 796         for (; gIter != NULL; gIter = gIter->next) {
 797             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 798             pe_node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
 799 
 800             if (chosen != NULL && is_set_recursive(child_rsc, pe_rsc_block, TRUE) == FALSE) {
 801                 pe_rsc_trace(primary, "Allowing %s: %s %d",
 802                              colocation->id, pe__node_name(chosen),
 803                              chosen->weight);
 804                 affected_nodes = g_list_prepend(affected_nodes, chosen);
 805             }
 806         }
 807 
 808         node_list_exclude(dependent->allowed_nodes, affected_nodes, FALSE);
 809         g_list_free(affected_nodes);
 810         return;
 811     }
 812 
 813     gIter = primary->children;
 814     for (; gIter != NULL; gIter = gIter->next) {
 815         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 816 
 817         child_rsc->cmds->apply_coloc_score(dependent, child_rsc, colocation,
 818                                            false);
 819     }
 820 }
 821 
 822 enum action_tasks
 823 clone_child_action(pe_action_t * action)
     /* [previous][next][first][last][top][bottom][index][help] */
 824 {
 825     enum action_tasks result = no_action;
 826     pe_resource_t *child = (pe_resource_t *) action->rsc->children->data;
 827 
 828     if (pcmk__strcase_any_of(action->task, "notify", "notified", NULL)) {
 829 
 830         /* Find the action we're notifying about instead */
 831 
 832         int stop = 0;
 833         char *key = action->uuid;
 834         int lpc = strlen(key);
 835 
 836         for (; lpc > 0; lpc--) {
 837             if (key[lpc] == '_' && stop == 0) {
 838                 stop = lpc;
 839 
 840             } else if (key[lpc] == '_') {
 841                 char *task_mutable = NULL;
 842 
 843                 lpc++;
 844                 task_mutable = strdup(key + lpc);
 845                 task_mutable[stop - lpc] = 0;
 846 
 847                 crm_trace("Extracted action '%s' from '%s'", task_mutable, key);
 848                 result = get_complex_task(child, task_mutable, TRUE);
 849                 free(task_mutable);
 850                 break;
 851             }
 852         }
 853 
 854     } else {
 855         result = get_complex_task(child, action->task, TRUE);
 856     }
 857     return result;
 858 }
 859 
 860 #define pe__clear_action_summary_flags(flags, action, flag) do {        \
 861         flags = pcmk__clear_flags_as(__func__, __LINE__, LOG_TRACE,     \
 862                                      "Action summary", action->rsc->id, \
 863                                      flags, flag, #flag);               \
 864     } while (0)
 865 
 866 enum pe_action_flags
 867 summary_action_flags(pe_action_t *action, GList *children,
     /* [previous][next][first][last][top][bottom][index][help] */
 868                      const pe_node_t *node)
 869 {
 870     GList *gIter = NULL;
 871     gboolean any_runnable = FALSE;
 872     gboolean check_runnable = TRUE;
 873     enum action_tasks task = clone_child_action(action);
 874     enum pe_action_flags flags = (pe_action_optional | pe_action_runnable | pe_action_pseudo);
 875     const char *task_s = task2text(task);
 876 
 877     for (gIter = children; gIter != NULL; gIter = gIter->next) {
 878         pe_action_t *child_action = NULL;
 879         pe_resource_t *child = (pe_resource_t *) gIter->data;
 880 
 881         child_action = find_first_action(child->actions, NULL, task_s, child->children ? NULL : node);
 882         pe_rsc_trace(action->rsc, "Checking for %s in %s on %s (%s)", task_s, child->id,
 883                      pe__node_name(node), child_action?child_action->uuid:"NA");
 884         if (child_action) {
 885             enum pe_action_flags child_flags = child->cmds->action_flags(child_action, node);
 886 
 887             if (pcmk_is_set(flags, pe_action_optional)
 888                 && !pcmk_is_set(child_flags, pe_action_optional)) {
 889                 pe_rsc_trace(child, "%s is mandatory because of %s", action->uuid,
 890                              child_action->uuid);
 891                 pe__clear_action_summary_flags(flags, action, pe_action_optional);
 892                 pe__clear_action_flags(action, pe_action_optional);
 893             }
 894             if (pcmk_is_set(child_flags, pe_action_runnable)) {
 895                 any_runnable = TRUE;
 896             }
 897         }
 898     }
 899 
 900     if (check_runnable && any_runnable == FALSE) {
 901         pe_rsc_trace(action->rsc, "%s is not runnable because no children are", action->uuid);
 902         pe__clear_action_summary_flags(flags, action, pe_action_runnable);
 903         if (node == NULL) {
 904             pe__clear_action_flags(action, pe_action_runnable);
 905         }
 906     }
 907 
 908     return flags;
 909 }
 910 
 911 enum pe_action_flags
 912 clone_action_flags(pe_action_t *action, const pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 913 {
 914     return summary_action_flags(action, action->rsc->children, node);
 915 }
 916 
 917 void
 918 clone_rsc_location(pe_resource_t *rsc, pe__location_t *constraint)
     /* [previous][next][first][last][top][bottom][index][help] */
 919 {
 920     GList *gIter = rsc->children;
 921 
 922     pe_rsc_trace(rsc, "Processing location constraint %s for %s", constraint->id, rsc->id);
 923 
 924     pcmk__apply_location(rsc, constraint);
 925 
 926     for (; gIter != NULL; gIter = gIter->next) {
 927         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 928 
 929         child_rsc->cmds->apply_location(child_rsc, constraint);
 930     }
 931 }
 932 
 933 /*!
 934  * \internal
 935  * \brief Add a resource's actions to the transition graph
 936  *
 937  * \param[in] rsc  Resource whose actions should be added
 938  */
 939 void
 940 clone_expand(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 941 {
 942     GList *gIter = NULL;
 943     clone_variant_data_t *clone_data = NULL;
 944 
 945     get_clone_variant_data(clone_data, rsc);
 946 
 947     g_list_foreach(rsc->actions, (GFunc) rsc->cmds->action_flags, NULL);
 948 
 949     pe__create_notifications(rsc, clone_data->start_notify);
 950     pe__create_notifications(rsc, clone_data->stop_notify);
 951     pe__create_notifications(rsc, clone_data->promote_notify);
 952     pe__create_notifications(rsc, clone_data->demote_notify);
 953 
 954     /* Now that the notifcations have been created we can expand the children */
 955 
 956     gIter = rsc->children;
 957     for (; gIter != NULL; gIter = gIter->next) {
 958         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 959 
 960         child_rsc->cmds->add_actions_to_graph(child_rsc);
 961     }
 962 
 963     pcmk__add_rsc_actions_to_graph(rsc);
 964 
 965     /* The notifications are in the graph now, we can destroy the notify_data */
 966     pe__free_notification_data(clone_data->demote_notify);
 967     clone_data->demote_notify = NULL;
 968     pe__free_notification_data(clone_data->stop_notify);
 969     clone_data->stop_notify = NULL;
 970     pe__free_notification_data(clone_data->start_notify);
 971     clone_data->start_notify = NULL;
 972     pe__free_notification_data(clone_data->promote_notify);
 973     clone_data->promote_notify = NULL;
 974 }
 975 
 976 // Check whether a resource or any of its children is known on node
 977 static bool
 978 rsc_known_on(const pe_resource_t *rsc, const pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 979 {
 980     if (rsc->children) {
 981         for (GList *child_iter = rsc->children; child_iter != NULL;
 982              child_iter = child_iter->next) {
 983 
 984             pe_resource_t *child = (pe_resource_t *) child_iter->data;
 985 
 986             if (rsc_known_on(child, node)) {
 987                 return TRUE;
 988             }
 989         }
 990 
 991     } else if (rsc->known_on) {
 992         GHashTableIter iter;
 993         pe_node_t *known_node = NULL;
 994 
 995         g_hash_table_iter_init(&iter, rsc->known_on);
 996         while (g_hash_table_iter_next(&iter, NULL, (gpointer *) &known_node)) {
 997             if (node->details == known_node->details) {
 998                 return TRUE;
 999             }
1000         }
1001     }
1002     return FALSE;
1003 }
1004 
1005 // Look for an instance of clone that is known on node
1006 static pe_resource_t *
1007 find_instance_on(const pe_resource_t *clone, const pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1008 {
1009     for (GList *gIter = clone->children; gIter != NULL; gIter = gIter->next) {
1010         pe_resource_t *child = (pe_resource_t *) gIter->data;
1011 
1012         if (rsc_known_on(child, node)) {
1013             return child;
1014         }
1015     }
1016     return NULL;
1017 }
1018 
1019 // For anonymous clones, only a single instance needs to be probed
1020 static bool
1021 probe_anonymous_clone(pe_resource_t *rsc, pe_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1022                       pe_working_set_t *data_set)
1023 {
1024     // First, check if we probed an instance on this node last time
1025     pe_resource_t *child = find_instance_on(rsc, node);
1026 
1027     // Otherwise, check if we plan to start an instance on this node
1028     if (child == NULL) {
1029         for (GList *child_iter = rsc->children; child_iter && !child;
1030              child_iter = child_iter->next) {
1031 
1032             pe_node_t *local_node = NULL;
1033             pe_resource_t *child_rsc = (pe_resource_t *) child_iter->data;
1034 
1035             if (child_rsc) { /* make clang analyzer happy */
1036                 local_node = child_rsc->fns->location(child_rsc, NULL, FALSE);
1037                 if (local_node && (local_node->details == node->details)) {
1038                     child = child_rsc;
1039                 }
1040             }
1041         }
1042     }
1043 
1044     // Otherwise, use the first clone instance
1045     if (child == NULL) {
1046         child = rsc->children->data;
1047     }
1048     CRM_ASSERT(child);
1049     return child->cmds->create_probe(child, node);
1050 }
1051 
1052 /*!
1053  * \internal
1054  *
1055  * \brief Schedule any probes needed for a resource on a node
1056  *
1057  * \param[in] rsc   Resource to create probe for
1058  * \param[in] node  Node to create probe on
1059  *
1060  * \return true if any probe was created, otherwise false
1061  */
1062 bool
1063 clone_create_probe(pe_resource_t *rsc, pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1064 {
1065     CRM_ASSERT(rsc);
1066 
1067     rsc->children = g_list_sort(rsc->children, pcmk__cmp_instance_number);
1068     if (rsc->children == NULL) {
1069         pe_warn("Clone %s has no children", rsc->id);
1070         return false;
1071     }
1072 
1073     if (rsc->exclusive_discover) {
1074         pe_node_t *allowed = g_hash_table_lookup(rsc->allowed_nodes, node->details->id);
1075         if (allowed && allowed->rsc_discover_mode != pe_discover_exclusive) {
1076             /* exclusive discover is enabled and this node is not marked
1077              * as a node this resource should be discovered on
1078              *
1079              * remove the node from allowed_nodes so that the
1080              * notification contains only nodes that we might ever run
1081              * on
1082              */
1083             g_hash_table_remove(rsc->allowed_nodes, node->details->id);
1084 
1085             /* Bit of a shortcut - might as well take it */
1086             return false;
1087         }
1088     }
1089 
1090     if (pcmk_is_set(rsc->flags, pe_rsc_unique)) {
1091         return pcmk__probe_resource_list(rsc->children, node);
1092     } else {
1093         return probe_anonymous_clone(rsc, node, rsc->cluster);
1094     }
1095 }
1096 
1097 void
1098 clone_append_meta(pe_resource_t * rsc, xmlNode * xml)
     /* [previous][next][first][last][top][bottom][index][help] */
1099 {
1100     char *name = NULL;
1101     clone_variant_data_t *clone_data = NULL;
1102 
1103     get_clone_variant_data(clone_data, rsc);
1104 
1105     name = crm_meta_name(XML_RSC_ATTR_UNIQUE);
1106     crm_xml_add(xml, name, pe__rsc_bool_str(rsc, pe_rsc_unique));
1107     free(name);
1108 
1109     name = crm_meta_name(XML_RSC_ATTR_NOTIFY);
1110     crm_xml_add(xml, name, pe__rsc_bool_str(rsc, pe_rsc_notify));
1111     free(name);
1112 
1113     name = crm_meta_name(XML_RSC_ATTR_INCARNATION_MAX);
1114     crm_xml_add_int(xml, name, clone_data->clone_max);
1115     free(name);
1116 
1117     name = crm_meta_name(XML_RSC_ATTR_INCARNATION_NODEMAX);
1118     crm_xml_add_int(xml, name, clone_data->clone_node_max);
1119     free(name);
1120 
1121     if (pcmk_is_set(rsc->flags, pe_rsc_promotable)) {
1122         int promoted_max = pe__clone_promoted_max(rsc);
1123         int promoted_node_max = pe__clone_promoted_node_max(rsc);
1124 
1125         name = crm_meta_name(XML_RSC_ATTR_PROMOTED_MAX);
1126         crm_xml_add_int(xml, name, promoted_max);
1127         free(name);
1128 
1129         name = crm_meta_name(XML_RSC_ATTR_PROMOTED_NODEMAX);
1130         crm_xml_add_int(xml, name, promoted_node_max);
1131         free(name);
1132 
1133         /* @COMPAT Maintain backward compatibility with resource agents that
1134          * expect the old names (deprecated since 2.0.0).
1135          */
1136         name = crm_meta_name(PCMK_XA_PROMOTED_MAX_LEGACY);
1137         crm_xml_add_int(xml, name, promoted_max);
1138         free(name);
1139 
1140         name = crm_meta_name(PCMK_XA_PROMOTED_NODE_MAX_LEGACY);
1141         crm_xml_add_int(xml, name, promoted_node_max);
1142         free(name);
1143     }
1144 }
1145 
1146 // Clone implementation of resource_alloc_functions_t:add_utilization()
1147 void
1148 pcmk__clone_add_utilization(const pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1149                             const pe_resource_t *orig_rsc, GList *all_rscs,
1150                             GHashTable *utilization)
1151 {
1152     bool existing = false;
1153     pe_resource_t *child = NULL;
1154 
1155     if (!pcmk_is_set(rsc->flags, pe_rsc_provisional)) {
1156         return;
1157     }
1158 
1159     // Look for any child already existing in the list
1160     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
1161         child = (pe_resource_t *) iter->data;
1162         if (g_list_find(all_rscs, child)) {
1163             existing = true; // Keep checking remaining children
1164         } else {
1165             // If this is a clone of a group, look for group's members
1166             for (GList *member_iter = child->children; member_iter != NULL;
1167                  member_iter = member_iter->next) {
1168 
1169                 pe_resource_t *member = (pe_resource_t *) member_iter->data;
1170 
1171                 if (g_list_find(all_rscs, member) != NULL) {
1172                     // Add *child's* utilization, not group member's
1173                     child->cmds->add_utilization(child, orig_rsc, all_rscs,
1174                                                  utilization);
1175                     existing = true;
1176                     break;
1177                 }
1178             }
1179         }
1180     }
1181 
1182     if (!existing && (rsc->children != NULL)) {
1183         // If nothing was found, still add first child's utilization
1184         child = (pe_resource_t *) rsc->children->data;
1185 
1186         child->cmds->add_utilization(child, orig_rsc, all_rscs, utilization);
1187     }
1188 }
1189 
1190 // Clone implementation of resource_alloc_functions_t:shutdown_lock()
1191 void
1192 pcmk__clone_shutdown_lock(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1193 {
1194     return; // Clones currently don't support shutdown locks
1195 }

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