This source file includes following definitions.
- pcmk__rsc_agent_changed
- add_rsc_if_matching
- pcmk__rscs_matching_id
- set_assignment_methods_for_rsc
- pcmk__set_assignment_methods
- add_colocated_resources
- pcmk__colocated_resources
- pcmk__noop_add_graph_meta
- pcmk__output_resource_actions
- add_assigned_resource
- pcmk__assign_resource
- pcmk__unassign_resource
- pcmk__threshold_reached
- get_node_score
- cmp_resources
- pcmk__sort_resources
   1 
   2 
   3 
   4 
   5 
   6 
   7 
   8 
   9 
  10 #include <crm_internal.h>
  11 
  12 #include <stdlib.h>
  13 #include <string.h>
  14 #include <crm/common/xml.h>
  15 #include <pacemaker-internal.h>
  16 
  17 #include "libpacemaker_private.h"
  18 
  19 
  20 static pcmk_assignment_methods_t assignment_methods[] = {
  21     {
  22         pcmk__primitive_assign,
  23         pcmk__primitive_create_actions,
  24         pcmk__probe_rsc_on_node,
  25         pcmk__primitive_internal_constraints,
  26         pcmk__primitive_apply_coloc_score,
  27         pcmk__colocated_resources,
  28         pcmk__with_primitive_colocations,
  29         pcmk__primitive_with_colocations,
  30         pcmk__add_colocated_node_scores,
  31         pcmk__apply_location,
  32         pcmk__primitive_action_flags,
  33         pcmk__update_ordered_actions,
  34         pcmk__output_resource_actions,
  35         pcmk__add_rsc_actions_to_graph,
  36         pcmk__primitive_add_graph_meta,
  37         pcmk__primitive_add_utilization,
  38         pcmk__primitive_shutdown_lock,
  39     },
  40     {
  41         pcmk__group_assign,
  42         pcmk__group_create_actions,
  43         pcmk__probe_rsc_on_node,
  44         pcmk__group_internal_constraints,
  45         pcmk__group_apply_coloc_score,
  46         pcmk__group_colocated_resources,
  47         pcmk__with_group_colocations,
  48         pcmk__group_with_colocations,
  49         pcmk__group_add_colocated_node_scores,
  50         pcmk__group_apply_location,
  51         pcmk__group_action_flags,
  52         pcmk__group_update_ordered_actions,
  53         pcmk__output_resource_actions,
  54         pcmk__add_rsc_actions_to_graph,
  55         pcmk__noop_add_graph_meta,
  56         pcmk__group_add_utilization,
  57         pcmk__group_shutdown_lock,
  58     },
  59     {
  60         pcmk__clone_assign,
  61         pcmk__clone_create_actions,
  62         pcmk__clone_create_probe,
  63         pcmk__clone_internal_constraints,
  64         pcmk__clone_apply_coloc_score,
  65         pcmk__colocated_resources,
  66         pcmk__with_clone_colocations,
  67         pcmk__clone_with_colocations,
  68         pcmk__add_colocated_node_scores,
  69         pcmk__clone_apply_location,
  70         pcmk__clone_action_flags,
  71         pcmk__instance_update_ordered_actions,
  72         pcmk__output_resource_actions,
  73         pcmk__clone_add_actions_to_graph,
  74         pcmk__clone_add_graph_meta,
  75         pcmk__clone_add_utilization,
  76         pcmk__clone_shutdown_lock,
  77     },
  78     {
  79         pcmk__bundle_assign,
  80         pcmk__bundle_create_actions,
  81         pcmk__bundle_create_probe,
  82         pcmk__bundle_internal_constraints,
  83         pcmk__bundle_apply_coloc_score,
  84         pcmk__colocated_resources,
  85         pcmk__with_bundle_colocations,
  86         pcmk__bundle_with_colocations,
  87         pcmk__add_colocated_node_scores,
  88         pcmk__bundle_apply_location,
  89         pcmk__bundle_action_flags,
  90         pcmk__instance_update_ordered_actions,
  91         pcmk__output_bundle_actions,
  92         pcmk__bundle_add_actions_to_graph,
  93         pcmk__noop_add_graph_meta,
  94         pcmk__bundle_add_utilization,
  95         pcmk__bundle_shutdown_lock,
  96     }
  97 };
  98 
  99 
 100 
 101 
 102 
 103 
 104 
 105 
 106 
 107 
 108 
 109 
 110 bool
 111 pcmk__rsc_agent_changed(pcmk_resource_t *rsc, pcmk_node_t *node,
     
 112                         const xmlNode *rsc_entry, bool active_on_node)
 113 {
 114     bool changed = false;
 115     const char *attr_list[] = {
 116         PCMK_XA_TYPE,
 117         PCMK_XA_CLASS,
 118         PCMK_XA_PROVIDER,
 119     };
 120 
 121     for (int i = 0; i < PCMK__NELEM(attr_list); i++) {
 122         const char *value = crm_element_value(rsc->xml, attr_list[i]);
 123         const char *old_value = crm_element_value(rsc_entry, attr_list[i]);
 124 
 125         if (!pcmk__str_eq(value, old_value, pcmk__str_none)) {
 126             changed = true;
 127             trigger_unfencing(rsc, node, "Device definition changed", NULL,
 128                               rsc->cluster);
 129             if (active_on_node) {
 130                 crm_notice("Forcing restart of %s on %s "
 131                            "because %s changed from '%s' to '%s'",
 132                            rsc->id, pcmk__node_name(node), attr_list[i],
 133                            pcmk__s(old_value, ""), pcmk__s(value, ""));
 134             }
 135         }
 136     }
 137     if (changed && active_on_node) {
 138         
 139         custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
 140                       rsc->cluster);
 141         pcmk__set_rsc_flags(rsc, pcmk_rsc_start_pending);
 142     }
 143     return changed;
 144 }
 145 
 146 
 147 
 148 
 149 
 150 
 151 
 152 
 153 
 154 
 155 
 156 static GList *
 157 add_rsc_if_matching(GList *result, pcmk_resource_t *rsc, const char *id)
     
 158 {
 159     if ((strcmp(rsc->id, id) == 0)
 160         || ((rsc->clone_name != NULL) && (strcmp(rsc->clone_name, id) == 0))) {
 161         result = g_list_prepend(result, rsc);
 162     }
 163     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 164         pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
 165 
 166         result = add_rsc_if_matching(result, child, id);
 167     }
 168     return result;
 169 }
 170 
 171 
 172 
 173 
 174 
 175 
 176 
 177 
 178 
 179 
 180 
 181 
 182 GList *
 183 pcmk__rscs_matching_id(const char *id, const pcmk_scheduler_t *scheduler)
     
 184 {
 185     GList *result = NULL;
 186 
 187     CRM_CHECK((id != NULL) && (scheduler != NULL), return NULL);
 188     for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
 189         result = add_rsc_if_matching(result, (pcmk_resource_t *) iter->data,
 190                                      id);
 191     }
 192     return result;
 193 }
 194 
 195 
 196 
 197 
 198 
 199 
 200 
 201 
 202 static void
 203 set_assignment_methods_for_rsc(gpointer data, gpointer user_data)
     
 204 {
 205     pcmk_resource_t *rsc = data;
 206 
 207     rsc->cmds = &assignment_methods[rsc->variant];
 208     g_list_foreach(rsc->children, set_assignment_methods_for_rsc, NULL);
 209 }
 210 
 211 
 212 
 213 
 214 
 215 
 216 
 217 void
 218 pcmk__set_assignment_methods(pcmk_scheduler_t *scheduler)
     
 219 {
 220     g_list_foreach(scheduler->resources, set_assignment_methods_for_rsc, NULL);
 221 }
 222 
 223 
 224 
 225 
 226 
 227 
 228 
 229 
 230 
 231 
 232 
 233 static inline void
 234 add_colocated_resources(const pcmk_resource_t *rsc,
     
 235                         const pcmk_resource_t *orig_rsc, GList **list)
 236 {
 237     *list = rsc->cmds->colocated_resources(rsc, orig_rsc, *list);
 238 }
 239 
 240 
 241 GList *
 242 pcmk__colocated_resources(const pcmk_resource_t *rsc,
     
 243                           const pcmk_resource_t *orig_rsc,
 244                           GList *colocated_rscs)
 245 {
 246     const GList *iter = NULL;
 247     GList *colocations = NULL;
 248 
 249     if (orig_rsc == NULL) {
 250         orig_rsc = rsc;
 251     }
 252 
 253     if ((rsc == NULL) || (g_list_find(colocated_rscs, rsc) != NULL)) {
 254         return colocated_rscs;
 255     }
 256 
 257     pcmk__rsc_trace(orig_rsc, "%s is in colocation chain with %s",
 258                     rsc->id, orig_rsc->id);
 259     colocated_rscs = g_list_prepend(colocated_rscs, (gpointer) rsc);
 260 
 261     
 262     colocations = pcmk__this_with_colocations(rsc);
 263     for (iter = colocations; iter != NULL; iter = iter->next) {
 264         const pcmk__colocation_t *constraint = iter->data;
 265         const pcmk_resource_t *primary = constraint->primary;
 266 
 267         if (primary == orig_rsc) {
 268             continue; 
 269         }
 270 
 271         if ((constraint->score == PCMK_SCORE_INFINITY) &&
 272             (pcmk__colocation_affects(rsc, primary, constraint,
 273                                       true) == pcmk__coloc_affects_location)) {
 274             add_colocated_resources(primary, orig_rsc, &colocated_rscs);
 275         }
 276     }
 277     g_list_free(colocations);
 278 
 279     
 280     colocations = pcmk__with_this_colocations(rsc);
 281     for (iter = colocations; iter != NULL; iter = iter->next) {
 282         const pcmk__colocation_t *constraint = iter->data;
 283         const pcmk_resource_t *dependent = constraint->dependent;
 284 
 285         if (dependent == orig_rsc) {
 286             continue; 
 287         }
 288 
 289         if (pcmk__is_clone(rsc) && !pcmk__is_clone(dependent)) {
 290             continue; 
 291         }
 292 
 293         if ((constraint->score == PCMK_SCORE_INFINITY) &&
 294             (pcmk__colocation_affects(dependent, rsc, constraint,
 295                                       true) == pcmk__coloc_affects_location)) {
 296             add_colocated_resources(dependent, orig_rsc, &colocated_rscs);
 297         }
 298     }
 299     g_list_free(colocations);
 300 
 301     return colocated_rscs;
 302 }
 303 
 304 
 305 void
 306 pcmk__noop_add_graph_meta(const pcmk_resource_t *rsc, xmlNode *xml)
     
 307 {
 308 }
 309 
 310 
 311 
 312 
 313 
 314 
 315 
 316 void
 317 pcmk__output_resource_actions(pcmk_resource_t *rsc)
     
 318 {
 319     pcmk_node_t *next = NULL;
 320     pcmk_node_t *current = NULL;
 321     pcmk__output_t *out = NULL;
 322 
 323     pcmk__assert(rsc != NULL);
 324 
 325     out = rsc->cluster->priv;
 326     if (rsc->children != NULL) {
 327         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 328             pcmk_resource_t *child = (pcmk_resource_t *) iter->data;
 329 
 330             child->cmds->output_actions(child);
 331         }
 332         return;
 333     }
 334 
 335     next = rsc->allocated_to;
 336     if (rsc->running_on) {
 337         current = pcmk__current_node(rsc);
 338         if (rsc->role == pcmk_role_stopped) {
 339             
 340 
 341 
 342             rsc->role = pcmk_role_started;
 343         }
 344     }
 345 
 346     if ((current == NULL) && pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
 347         
 348         return;
 349     }
 350 
 351     out->message(out, "rsc-action", rsc, current, next);
 352 }
 353 
 354 
 355 
 356 
 357 
 358 
 359 
 360 
 361 static inline void
 362 add_assigned_resource(pcmk_node_t *node, pcmk_resource_t *rsc)
     
 363 {
 364     node->details->allocated_rsc = g_list_prepend(node->details->allocated_rsc,
 365                                                   rsc);
 366 }
 367 
 368 
 369 
 370 
 371 
 372 
 373 
 374 
 375 
 376 
 377 
 378 
 379 
 380 
 381 
 382 
 383 
 384 
 385 
 386 
 387 
 388 
 389 
 390 
 391 
 392 
 393 
 394 
 395 
 396 
 397 
 398 
 399 
 400 
 401 
 402 
 403 bool
 404 pcmk__assign_resource(pcmk_resource_t *rsc, pcmk_node_t *node, bool force,
     
 405                       bool stop_if_fail)
 406 {
 407     bool changed = false;
 408 
 409     pcmk__assert(rsc != NULL);
 410 
 411     if (rsc->children != NULL) {
 412         for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 413             pcmk_resource_t *child_rsc = iter->data;
 414 
 415             changed |= pcmk__assign_resource(child_rsc, node, force,
 416                                              stop_if_fail);
 417         }
 418         return changed;
 419     }
 420 
 421     
 422 
 423     if (!force && (node != NULL)
 424         && ((node->weight < 0)
 425             
 426             || (!pcmk__node_available(node, true, false)
 427                 && !pcmk__is_guest_or_bundle_node(node)))) {
 428 
 429         pcmk__rsc_debug(rsc,
 430                         "All nodes for resource %s are unavailable, unclean or "
 431                         "shutting down (%s can%s run resources, with score %s)",
 432                         rsc->id, pcmk__node_name(node),
 433                         (pcmk__node_available(node, true, false)? "" : "not"),
 434                         pcmk_readable_score(node->weight));
 435 
 436         if (stop_if_fail) {
 437             pe__set_next_role(rsc, pcmk_role_stopped, "node availability");
 438         }
 439         node = NULL;
 440     }
 441 
 442     if (rsc->allocated_to != NULL) {
 443         changed = !pcmk__same_node(rsc->allocated_to, node);
 444     } else {
 445         changed = (node != NULL);
 446     }
 447     pcmk__unassign_resource(rsc);
 448     pcmk__clear_rsc_flags(rsc, pcmk_rsc_unassigned);
 449 
 450     if (node == NULL) {
 451         char *rc_stopped = NULL;
 452 
 453         pcmk__rsc_debug(rsc, "Could not assign %s to a node", rsc->id);
 454 
 455         if (!stop_if_fail) {
 456             return changed;
 457         }
 458         pe__set_next_role(rsc, pcmk_role_stopped, "unable to assign");
 459 
 460         for (GList *iter = rsc->actions; iter != NULL; iter = iter->next) {
 461             pcmk_action_t *op = (pcmk_action_t *) iter->data;
 462 
 463             pcmk__rsc_debug(rsc, "Updating %s for %s assignment failure",
 464                             op->uuid, rsc->id);
 465 
 466             if (pcmk__str_eq(op->task, PCMK_ACTION_STOP, pcmk__str_none)) {
 467                 pcmk__clear_action_flags(op, pcmk_action_optional);
 468 
 469             } else if (pcmk__str_eq(op->task, PCMK_ACTION_START,
 470                                     pcmk__str_none)) {
 471                 pcmk__clear_action_flags(op, pcmk_action_runnable);
 472 
 473             } else {
 474                 
 475                 const char *interval_ms_s = NULL;
 476                 const char *target_rc_s = NULL;
 477 
 478                 interval_ms_s = g_hash_table_lookup(op->meta,
 479                                                     PCMK_META_INTERVAL);
 480                 target_rc_s = g_hash_table_lookup(op->meta,
 481                                                   PCMK__META_OP_TARGET_RC);
 482                 if (rc_stopped == NULL) {
 483                     rc_stopped = pcmk__itoa(PCMK_OCF_NOT_RUNNING);
 484                 }
 485 
 486                 if (!pcmk__str_eq(interval_ms_s, "0", pcmk__str_null_matches)
 487                     && !pcmk__str_eq(rc_stopped, target_rc_s, pcmk__str_none)) {
 488 
 489                     pcmk__clear_action_flags(op, pcmk_action_runnable);
 490                 }
 491             }
 492         }
 493         free(rc_stopped);
 494         return changed;
 495     }
 496 
 497     pcmk__rsc_debug(rsc, "Assigning %s to %s", rsc->id, pcmk__node_name(node));
 498     rsc->allocated_to = pe__copy_node(node);
 499 
 500     add_assigned_resource(node, rsc);
 501     node->details->num_resources++;
 502     node->count++;
 503     pcmk__consume_node_capacity(node->details->utilization, rsc);
 504 
 505     if (pcmk_is_set(rsc->cluster->flags, pcmk_sched_show_utilization)) {
 506         pcmk__output_t *out = rsc->cluster->priv;
 507 
 508         out->message(out, "resource-util", rsc, node, __func__);
 509     }
 510     return changed;
 511 }
 512 
 513 
 514 
 515 
 516 
 517 
 518 
 519 
 520 
 521 
 522 
 523 
 524 void
 525 pcmk__unassign_resource(pcmk_resource_t *rsc)
     
 526 {
 527     pcmk_node_t *old = rsc->allocated_to;
 528 
 529     if (old == NULL) {
 530         crm_info("Unassigning %s", rsc->id);
 531     } else {
 532         crm_info("Unassigning %s from %s", rsc->id, pcmk__node_name(old));
 533     }
 534 
 535     pcmk__set_rsc_flags(rsc, pcmk_rsc_unassigned);
 536 
 537     if (rsc->children == NULL) {
 538         if (old == NULL) {
 539             return;
 540         }
 541         rsc->allocated_to = NULL;
 542 
 543         
 544 
 545 
 546         old->details->allocated_rsc = g_list_remove(old->details->allocated_rsc,
 547                                                     rsc);
 548         old->details->num_resources--;
 549         pcmk__release_node_capacity(old->details->utilization, rsc);
 550         free(old);
 551         return;
 552     }
 553 
 554     for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
 555         pcmk__unassign_resource((pcmk_resource_t *) iter->data);
 556     }
 557 }
 558 
 559 
 560 
 561 
 562 
 563 
 564 
 565 
 566 
 567 
 568 
 569 
 570 bool
 571 pcmk__threshold_reached(pcmk_resource_t *rsc, const pcmk_node_t *node,
     
 572                         pcmk_resource_t **failed)
 573 {
 574     int fail_count, remaining_tries;
 575     pcmk_resource_t *rsc_to_ban = rsc;
 576 
 577     
 578     if (rsc->migration_threshold == 0) {
 579         return false;
 580     }
 581 
 582     
 583     if (pcmk_is_set(rsc->flags, pcmk_rsc_ignore_failure)) {
 584         return false;
 585     }
 586 
 587     
 588     fail_count = pe_get_failcount(node, rsc, NULL,
 589                                   pcmk__fc_effective|pcmk__fc_fillers, NULL);
 590     if (fail_count <= 0) {
 591         return false;
 592     }
 593 
 594     
 595     if (!pcmk_is_set(rsc->flags, pcmk_rsc_unique)) {
 596         rsc_to_ban = uber_parent(rsc);
 597     }
 598 
 599     
 600     remaining_tries = rsc->migration_threshold - fail_count;
 601 
 602     if (remaining_tries <= 0) {
 603         pcmk__sched_warn("%s cannot run on %s due to reaching migration "
 604                          "threshold (clean up resource to allow again) "
 605                          CRM_XS " failures=%d "
 606                          PCMK_META_MIGRATION_THRESHOLD "=%d",
 607                          rsc_to_ban->id, pcmk__node_name(node), fail_count,
 608                          rsc->migration_threshold);
 609         if (failed != NULL) {
 610             *failed = rsc_to_ban;
 611         }
 612         return true;
 613     }
 614 
 615     crm_info("%s can fail %d more time%s on "
 616              "%s before reaching migration threshold (%d)",
 617              rsc_to_ban->id, remaining_tries, pcmk__plural_s(remaining_tries),
 618              pcmk__node_name(node), rsc->migration_threshold);
 619     return false;
 620 }
 621 
 622 
 623 
 624 
 625 
 626 
 627 
 628 
 629 
 630 
 631 static int
 632 get_node_score(const pcmk_node_t *node, GHashTable *nodes)
     
 633 {
 634     pcmk_node_t *found_node = NULL;
 635 
 636     if ((node != NULL) && (nodes != NULL)) {
 637         found_node = g_hash_table_lookup(nodes, node->details->id);
 638     }
 639     return (found_node == NULL)? -PCMK_SCORE_INFINITY : found_node->weight;
 640 }
 641 
 642 
 643 
 644 
 645 
 646 
 647 
 648 
 649 
 650 
 651 
 652 
 653 static gint
 654 cmp_resources(gconstpointer a, gconstpointer b, gpointer data)
     
 655 {
 656     
 657 
 658 
 659 
 660     pcmk_resource_t *resource1 = (pcmk_resource_t *) a;
 661     pcmk_resource_t *resource2 = (pcmk_resource_t *) b;
 662     const GList *nodes = data;
 663 
 664     int rc = 0;
 665     int r1_score = -PCMK_SCORE_INFINITY;
 666     int r2_score = -PCMK_SCORE_INFINITY;
 667     pcmk_node_t *r1_node = NULL;
 668     pcmk_node_t *r2_node = NULL;
 669     GHashTable *r1_nodes = NULL;
 670     GHashTable *r2_nodes = NULL;
 671     const char *reason = NULL;
 672 
 673     
 674     reason = "priority";
 675     r1_score = resource1->priority;
 676     r2_score = resource2->priority;
 677     if (r1_score > r2_score) {
 678         rc = -1;
 679         goto done;
 680     }
 681     if (r1_score < r2_score) {
 682         rc = 1;
 683         goto done;
 684     }
 685 
 686     
 687     reason = "no node list";
 688     if (nodes == NULL) {
 689         goto done;
 690     }
 691 
 692     
 693     resource1->cmds->add_colocated_node_scores(resource1, NULL, resource1->id,
 694                                                &r1_nodes, NULL, 1,
 695                                                pcmk__coloc_select_this_with);
 696     resource2->cmds->add_colocated_node_scores(resource2, NULL, resource2->id,
 697                                                &r2_nodes, NULL, 1,
 698                                                pcmk__coloc_select_this_with);
 699     pe__show_node_scores(true, NULL, resource1->id, r1_nodes,
 700                          resource1->cluster);
 701     pe__show_node_scores(true, NULL, resource2->id, r2_nodes,
 702                          resource2->cluster);
 703 
 704     
 705     reason = "current location";
 706     if (resource1->running_on != NULL) {
 707         r1_node = pcmk__current_node(resource1);
 708     }
 709     if (resource2->running_on != NULL) {
 710         r2_node = pcmk__current_node(resource2);
 711     }
 712     r1_score = get_node_score(r1_node, r1_nodes);
 713     r2_score = get_node_score(r2_node, r2_nodes);
 714     if (r1_score > r2_score) {
 715         rc = -1;
 716         goto done;
 717     }
 718     if (r1_score < r2_score) {
 719         rc = 1;
 720         goto done;
 721     }
 722 
 723     
 724     reason = "score";
 725     for (const GList *iter = nodes; iter != NULL; iter = iter->next) {
 726         const pcmk_node_t *node = (const pcmk_node_t *) iter->data;
 727 
 728         r1_score = get_node_score(node, r1_nodes);
 729         r2_score = get_node_score(node, r2_nodes);
 730         if (r1_score > r2_score) {
 731             rc = -1;
 732             goto done;
 733         }
 734         if (r1_score < r2_score) {
 735             rc = 1;
 736             goto done;
 737         }
 738     }
 739 
 740 done:
 741     crm_trace("%s (%d)%s%s %c %s (%d)%s%s: %s",
 742               resource1->id, r1_score,
 743               ((r1_node == NULL)? "" : " on "),
 744               ((r1_node == NULL)? "" : r1_node->details->id),
 745               ((rc < 0)? '>' : ((rc > 0)? '<' : '=')),
 746               resource2->id, r2_score,
 747               ((r2_node == NULL)? "" : " on "),
 748               ((r2_node == NULL)? "" : r2_node->details->id),
 749               reason);
 750     if (r1_nodes != NULL) {
 751         g_hash_table_destroy(r1_nodes);
 752     }
 753     if (r2_nodes != NULL) {
 754         g_hash_table_destroy(r2_nodes);
 755     }
 756     return rc;
 757 }
 758 
 759 
 760 
 761 
 762 
 763 
 764 
 765 void
 766 pcmk__sort_resources(pcmk_scheduler_t *scheduler)
     
 767 {
 768     GList *nodes = g_list_copy(scheduler->nodes);
 769 
 770     nodes = pcmk__sort_nodes(nodes, NULL);
 771     scheduler->resources = g_list_sort_with_data(scheduler->resources,
 772                                                  cmp_resources, nodes);
 773     g_list_free(nodes);
 774 }