root/lib/pacemaker/pcmk_sched_promotable.c

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

DEFINITIONS

This source file includes following definitions.
  1. child_promoting_constraints
  2. child_demoting_constraints
  3. check_promotable_actions
  4. apply_promoted_location
  5. guest_location
  6. node_to_be_promoted_on
  7. sort_promotable_instance
  8. promotion_order
  9. filter_anonymous_instance
  10. lookup_promotion_score
  11. promotion_score
  12. pcmk__add_promotion_scores
  13. set_role_unpromoted
  14. set_role_promoted
  15. pcmk__set_instance_roles
  16. create_promotable_actions
  17. promote_demote_constraints
  18. promotable_constraints
  19. node_hash_update_one
  20. promotable_colocation_rh

   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 extern gint sort_clone_instance(gconstpointer a, gconstpointer b, gpointer data_set);
  21 
  22 extern bool pcmk__is_daemon;
  23 
  24 static void
  25 child_promoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type,
     /* [previous][next][first][last][top][bottom][index][help] */
  26                             pe_resource_t * rsc, pe_resource_t * child, pe_resource_t * last,
  27                             pe_working_set_t * data_set)
  28 {
  29     if (child == NULL) {
  30         if (clone_data->ordered && last != NULL) {
  31             pe_rsc_trace(rsc, "Ordered version (last node)");
  32             /* last child promote before promoted started */
  33             pcmk__order_resource_actions(last, RSC_PROMOTE, rsc, RSC_PROMOTED,
  34                                          type, data_set);
  35         }
  36         return;
  37     }
  38 
  39     /* child promote before global promoted */
  40     pcmk__order_resource_actions(child, RSC_PROMOTE, rsc, RSC_PROMOTED, type,
  41                                  data_set);
  42 
  43     /* global promote before child promote */
  44     pcmk__order_resource_actions(rsc, RSC_PROMOTE, child, RSC_PROMOTE, type,
  45                                  data_set);
  46 
  47     if (clone_data->ordered) {
  48         pe_rsc_trace(rsc, "Ordered version");
  49         if (last == NULL) {
  50             /* global promote before first child promote */
  51             last = rsc;
  52 
  53         }
  54         /* else: child/child relative promote */
  55         pcmk__order_starts(last, child, type, data_set);
  56         pcmk__order_resource_actions(last, RSC_PROMOTE, child, RSC_PROMOTE,
  57                                      type, data_set);
  58 
  59     } else {
  60         pe_rsc_trace(rsc, "Un-ordered version");
  61     }
  62 }
  63 
  64 static void
  65 child_demoting_constraints(clone_variant_data_t * clone_data, enum pe_ordering type,
     /* [previous][next][first][last][top][bottom][index][help] */
  66                            pe_resource_t * rsc, pe_resource_t * child, pe_resource_t * last,
  67                            pe_working_set_t * data_set)
  68 {
  69     if (child == NULL) {
  70         if (clone_data->ordered && last != NULL) {
  71             pe_rsc_trace(rsc, "Ordered version (last node)");
  72             /* global demote before first child demote */
  73             pcmk__order_resource_actions(rsc, RSC_DEMOTE, last, RSC_DEMOTE,
  74                                          pe_order_optional, data_set);
  75         }
  76         return;
  77     }
  78 
  79     /* child demote before global demoted */
  80     pcmk__order_resource_actions(child, RSC_DEMOTE, rsc, RSC_DEMOTED,
  81                                  pe_order_implies_then_printed, data_set);
  82 
  83     /* global demote before child demote */
  84     pcmk__order_resource_actions(rsc, RSC_DEMOTE, child, RSC_DEMOTE,
  85                                  pe_order_implies_first_printed, data_set);
  86 
  87     if (clone_data->ordered && last != NULL) {
  88         pe_rsc_trace(rsc, "Ordered version");
  89 
  90         /* child/child relative demote */
  91         pcmk__order_resource_actions(child, RSC_DEMOTE, last, RSC_DEMOTE, type,
  92                                      data_set);
  93 
  94     } else if (clone_data->ordered) {
  95         pe_rsc_trace(rsc, "Ordered version (1st node)");
  96         /* first child stop before global stopped */
  97         pcmk__order_resource_actions(child, RSC_DEMOTE, rsc, RSC_DEMOTED, type,
  98                                      data_set);
  99 
 100     } else {
 101         pe_rsc_trace(rsc, "Un-ordered version");
 102     }
 103 }
 104 
 105 static void
 106 check_promotable_actions(pe_resource_t *rsc, gboolean *demoting,
     /* [previous][next][first][last][top][bottom][index][help] */
 107                          gboolean *promoting)
 108 {
 109     GList *gIter = NULL;
 110 
 111     if (rsc->children) {
 112         gIter = rsc->children;
 113         for (; gIter != NULL; gIter = gIter->next) {
 114             pe_resource_t *child = (pe_resource_t *) gIter->data;
 115 
 116             check_promotable_actions(child, demoting, promoting);
 117         }
 118         return;
 119     }
 120 
 121     CRM_ASSERT(demoting != NULL);
 122     CRM_ASSERT(promoting != NULL);
 123 
 124     gIter = rsc->actions;
 125     for (; gIter != NULL; gIter = gIter->next) {
 126         pe_action_t *action = (pe_action_t *) gIter->data;
 127 
 128         if (*promoting && *demoting) {
 129             return;
 130 
 131         } else if (pcmk_is_set(action->flags, pe_action_optional)) {
 132             continue;
 133 
 134         } else if (pcmk__str_eq(RSC_DEMOTE, action->task, pcmk__str_casei)) {
 135             *demoting = TRUE;
 136 
 137         } else if (pcmk__str_eq(RSC_PROMOTE, action->task, pcmk__str_casei)) {
 138             *promoting = TRUE;
 139         }
 140     }
 141 }
 142 
 143 static void
 144 apply_promoted_location(pe_resource_t *child, GList *location_constraints,
     /* [previous][next][first][last][top][bottom][index][help] */
 145                         pe_node_t *chosen)
 146 {
 147     CRM_CHECK(child && chosen, return);
 148     for (GList *gIter = location_constraints; gIter; gIter = gIter->next) {
 149         pe_node_t *cons_node = NULL;
 150         pe__location_t *cons = gIter->data;
 151 
 152         if (cons->role_filter == RSC_ROLE_PROMOTED) {
 153             pe_rsc_trace(child, "Applying %s to %s", cons->id, child->id);
 154             cons_node = pe_find_node_id(cons->node_list_rh, chosen->details->id);
 155         }
 156         if (cons_node != NULL) {
 157             int new_priority = pcmk__add_scores(child->priority,
 158                                                 cons_node->weight);
 159 
 160             pe_rsc_trace(child, "\t%s[%s]: %d -> %d (%d)",
 161                          child->id, cons_node->details->uname, child->priority,
 162                          new_priority, cons_node->weight);
 163             child->priority = new_priority;
 164         }
 165     }
 166 }
 167 
 168 static pe_node_t *
 169 guest_location(pe_node_t *guest_node)
     /* [previous][next][first][last][top][bottom][index][help] */
 170 {
 171     pe_resource_t *guest = guest_node->details->remote_rsc->container;
 172 
 173     return guest->fns->location(guest, NULL, FALSE);
 174 }
 175 
 176 static pe_node_t *
 177 node_to_be_promoted_on(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 178 {
 179     pe_node_t *node = NULL;
 180     pe_node_t *local_node = NULL;
 181     pe_resource_t *parent = uber_parent(rsc);
 182     clone_variant_data_t *clone_data = NULL;
 183 
 184 #if 0
 185     enum rsc_role_e role = RSC_ROLE_UNKNOWN;
 186 
 187     role = rsc->fns->state(rsc, FALSE);
 188     crm_info("%s role: %s", rsc->id, role2text(role));
 189 #endif
 190 
 191     if (rsc->children) {
 192         GList *gIter = rsc->children;
 193 
 194         for (; gIter != NULL; gIter = gIter->next) {
 195             pe_resource_t *child = (pe_resource_t *) gIter->data;
 196 
 197             if (node_to_be_promoted_on(child) == NULL) {
 198                 pe_rsc_trace(rsc, "Child %s of %s can't be promoted", child->id, rsc->id);
 199                 return NULL;
 200             }
 201         }
 202     }
 203 
 204     node = rsc->fns->location(rsc, NULL, FALSE);
 205     if (node == NULL) {
 206         pe_rsc_trace(rsc, "%s cannot be promoted: not allocated", rsc->id);
 207         return NULL;
 208 
 209     } else if (!pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 210         if (rsc->fns->state(rsc, TRUE) == RSC_ROLE_PROMOTED) {
 211             crm_notice("Forcing unmanaged instance %s to remain promoted on %s",
 212                        rsc->id, node->details->uname);
 213 
 214         } else {
 215             return NULL;
 216         }
 217 
 218     } else if (rsc->priority < 0) {
 219         pe_rsc_trace(rsc, "%s cannot be promoted: preference: %d",
 220                      rsc->id, rsc->priority);
 221         return NULL;
 222 
 223     } else if (!pcmk__node_available(node)) {
 224         crm_trace("Node can't run any resources: %s", node->details->uname);
 225         return NULL;
 226 
 227     /* @TODO It's possible this check should be done in pcmk__node_available()
 228      * instead. We should investigate all its callers to figure out whether that
 229      * would be a good idea.
 230      */
 231     } else if (pe__is_guest_node(node) && (guest_location(node) == NULL)) {
 232         pe_rsc_trace(rsc, "%s cannot be promoted: guest %s not allocated",
 233                      rsc->id, node->details->remote_rsc->container->id);
 234         return NULL;
 235     }
 236 
 237     get_clone_variant_data(clone_data, parent);
 238     local_node = pe_hash_table_lookup(parent->allowed_nodes, node->details->id);
 239 
 240     if (local_node == NULL) {
 241         crm_err("%s cannot run on %s: node not allowed", rsc->id, node->details->uname);
 242         return NULL;
 243 
 244     } else if ((local_node->count < clone_data->promoted_node_max)
 245                || !pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 246         return local_node;
 247 
 248     } else {
 249         pe_rsc_trace(rsc, "%s cannot be promoted on %s: node full",
 250                      rsc->id, node->details->uname);
 251     }
 252 
 253     return NULL;
 254 }
 255 
 256 static gint
 257 sort_promotable_instance(gconstpointer a, gconstpointer b, gpointer data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259     int rc;
 260     enum rsc_role_e role1 = RSC_ROLE_UNKNOWN;
 261     enum rsc_role_e role2 = RSC_ROLE_UNKNOWN;
 262 
 263     const pe_resource_t *resource1 = (const pe_resource_t *)a;
 264     const pe_resource_t *resource2 = (const pe_resource_t *)b;
 265 
 266     CRM_ASSERT(resource1 != NULL);
 267     CRM_ASSERT(resource2 != NULL);
 268 
 269     role1 = resource1->fns->state(resource1, TRUE);
 270     role2 = resource2->fns->state(resource2, TRUE);
 271 
 272     rc = sort_rsc_index(a, b);
 273     if (rc != 0) {
 274         crm_trace("%s %c %s (index)", resource1->id, rc < 0 ? '<' : '>', resource2->id);
 275         return rc;
 276     }
 277 
 278     if (role1 > role2) {
 279         crm_trace("%s %c %s (role)", resource1->id, '<', resource2->id);
 280         return -1;
 281 
 282     } else if (role1 < role2) {
 283         crm_trace("%s %c %s (role)", resource1->id, '>', resource2->id);
 284         return 1;
 285     }
 286 
 287     return sort_clone_instance(a, b, data_set);
 288 }
 289 
 290 static void
 291 promotion_order(pe_resource_t *rsc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 292 {
 293     GList *gIter = NULL;
 294     pe_node_t *node = NULL;
 295     pe_node_t *chosen = NULL;
 296     clone_variant_data_t *clone_data = NULL;
 297     char score[33];
 298     size_t len = sizeof(score);
 299 
 300     get_clone_variant_data(clone_data, rsc);
 301 
 302     if (clone_data->added_promoted_constraints) {
 303         return;
 304     }
 305     clone_data->added_promoted_constraints = true;
 306     pe_rsc_trace(rsc, "Merging weights for %s", rsc->id);
 307     pe__set_resource_flags(rsc, pe_rsc_merging);
 308 
 309     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 310         pe_resource_t *child = (pe_resource_t *) gIter->data;
 311 
 312         pe_rsc_trace(rsc, "Sort index: %s = %d", child->id, child->sort_index);
 313     }
 314     pe__show_node_weights(true, rsc, "Before", rsc->allowed_nodes, data_set);
 315 
 316     gIter = rsc->children;
 317     for (; gIter != NULL; gIter = gIter->next) {
 318         pe_resource_t *child = (pe_resource_t *) gIter->data;
 319 
 320         chosen = child->fns->location(child, NULL, FALSE);
 321         if (chosen == NULL || child->sort_index < 0) {
 322             pe_rsc_trace(rsc, "Skipping %s", child->id);
 323             continue;
 324         }
 325 
 326         node = (pe_node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id);
 327         CRM_ASSERT(node != NULL);
 328         // Add promotion preferences and rsc_location scores when role=Promoted
 329         score2char_stack(child->sort_index, score, len);
 330         pe_rsc_trace(rsc, "Adding %s to %s from %s", score,
 331                      node->details->uname, child->id);
 332         node->weight = pcmk__add_scores(child->sort_index, node->weight);
 333     }
 334 
 335     pe__show_node_weights(true, rsc, "Middle", rsc->allowed_nodes, data_set);
 336 
 337     gIter = rsc->rsc_cons;
 338     for (; gIter != NULL; gIter = gIter->next) {
 339         pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
 340 
 341         /* (Re-)add location preferences of resources that a promoted instance
 342          * should/must be colocated with.
 343          */
 344         if (constraint->dependent_role == RSC_ROLE_PROMOTED) {
 345             enum pe_weights flags = constraint->score == INFINITY ? 0 : pe_weights_rollback;
 346 
 347             pe_rsc_trace(rsc, "RHS: %s with %s: %d",
 348                          constraint->dependent->id, constraint->primary->id,
 349                          constraint->score);
 350             rsc->allowed_nodes = constraint->primary->cmds->merge_weights(
 351                 constraint->primary, rsc->id, rsc->allowed_nodes,
 352                 constraint->node_attribute,
 353                 constraint->score / (float) INFINITY, flags);
 354         }
 355     }
 356 
 357     gIter = rsc->rsc_cons_lhs;
 358     for (; gIter != NULL; gIter = gIter->next) {
 359         pcmk__colocation_t *constraint = (pcmk__colocation_t *) gIter->data;
 360 
 361         if (!pcmk__colocation_has_influence(constraint, NULL)) {
 362             continue;
 363         }
 364 
 365         /* (Re-)add location preferences of resources that wish to be colocated
 366          * with a promoted instance.
 367          */
 368         if (constraint->primary_role == RSC_ROLE_PROMOTED) {
 369             pe_rsc_trace(rsc, "LHS: %s with %s: %d",
 370                          constraint->dependent->id, constraint->primary->id,
 371                          constraint->score);
 372             rsc->allowed_nodes = constraint->dependent->cmds->merge_weights(
 373                 constraint->dependent, rsc->id, rsc->allowed_nodes,
 374                 constraint->node_attribute,
 375                 constraint->score / (float) INFINITY,
 376                 pe_weights_rollback|pe_weights_positive);
 377         }
 378     }
 379 
 380     gIter = rsc->rsc_tickets;
 381     for (; gIter != NULL; gIter = gIter->next) {
 382         rsc_ticket_t *rsc_ticket = (rsc_ticket_t *) gIter->data;
 383 
 384         if ((rsc_ticket->role_lh == RSC_ROLE_PROMOTED)
 385             && (rsc_ticket->ticket->granted == FALSE || rsc_ticket->ticket->standby)) {
 386             resource_location(rsc, NULL, -INFINITY, "__stateful_without_ticket__", data_set);
 387         }
 388     }
 389 
 390     pe__show_node_weights(true, rsc, "After", rsc->allowed_nodes, data_set);
 391 
 392     /* write them back and sort */
 393 
 394     gIter = rsc->children;
 395     for (; gIter != NULL; gIter = gIter->next) {
 396         pe_resource_t *child = (pe_resource_t *) gIter->data;
 397 
 398         chosen = child->fns->location(child, NULL, FALSE);
 399         if (!pcmk_is_set(child->flags, pe_rsc_managed)
 400             && (child->next_role == RSC_ROLE_PROMOTED)) {
 401             child->sort_index = INFINITY;
 402 
 403         } else if (chosen == NULL || child->sort_index < 0) {
 404             pe_rsc_trace(rsc, "%s: %d", child->id, child->sort_index);
 405 
 406         } else {
 407             node = (pe_node_t *) pe_hash_table_lookup(rsc->allowed_nodes, chosen->details->id);
 408             CRM_ASSERT(node != NULL);
 409 
 410             child->sort_index = node->weight;
 411         }
 412         pe_rsc_trace(rsc, "Set sort index: %s = %d", child->id, child->sort_index);
 413     }
 414 
 415     rsc->children = g_list_sort_with_data(rsc->children,
 416                                           sort_promotable_instance, data_set);
 417     pe__clear_resource_flags(rsc, pe_rsc_merging);
 418 }
 419 
 420 static gboolean
 421 filter_anonymous_instance(pe_resource_t *rsc, const pe_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
 422 {
 423     GList *rIter = NULL;
 424     char *key = clone_strip(rsc->id);
 425     pe_resource_t *parent = uber_parent(rsc);
 426 
 427     for (rIter = parent->children; rIter; rIter = rIter->next) {
 428         /* If there is an active instance on the node, only it receives the
 429          * promotion score. Use ->find_rsc() in case this is a cloned group.
 430          */
 431         pe_resource_t *child = rIter->data;
 432         pe_resource_t *active = parent->fns->find_rsc(child, key, node, pe_find_clone|pe_find_current);
 433 
 434         if(rsc == active) {
 435             pe_rsc_trace(rsc, "Found %s for %s active on %s: done", active->id, key, node->details->uname);
 436             free(key);
 437             return TRUE;
 438         } else if(active) {
 439             pe_rsc_trace(rsc, "Found %s for %s on %s: not %s", active->id, key, node->details->uname, rsc->id);
 440             free(key);
 441             return FALSE;
 442         } else {
 443             pe_rsc_trace(rsc, "%s on %s: not active", key, node->details->uname);
 444         }
 445     }
 446 
 447     for (rIter = parent->children; rIter; rIter = rIter->next) {
 448         pe_resource_t *child = rIter->data;
 449 
 450         /*
 451          * We know it's not running, but any score will still count if
 452          * the instance has been probed on $node
 453          *
 454          * Again use ->find_rsc() because we might be a cloned group
 455          * and knowing that other members of the group are known here
 456          * implies nothing
 457          */
 458         rsc = parent->fns->find_rsc(child, key, NULL, pe_find_clone);
 459         CRM_LOG_ASSERT(rsc);
 460         if(rsc) {
 461             pe_rsc_trace(rsc, "Checking %s for %s on %s", rsc->id, key, node->details->uname);
 462             if (g_hash_table_lookup(rsc->known_on, node->details->id)) {
 463                 free(key);
 464                 return TRUE;
 465             }
 466         }
 467     }
 468     free(key);
 469     return FALSE;
 470 }
 471 
 472 static const char *
 473 lookup_promotion_score(pe_resource_t *rsc, const pe_node_t *node, const char *name)
     /* [previous][next][first][last][top][bottom][index][help] */
 474 {
 475     const char *attr_value = NULL;
 476 
 477     if (node && name) {
 478         char *attr_name = pcmk_promotion_score_name(name);
 479 
 480         attr_value = pe_node_attribute_calculated(node, attr_name, rsc);
 481         free(attr_name);
 482     }
 483     return attr_value;
 484 }
 485 
 486 static int
 487 promotion_score(pe_resource_t *rsc, const pe_node_t *node, int not_set_value)
     /* [previous][next][first][last][top][bottom][index][help] */
 488 {
 489     char *name = rsc->id;
 490     const char *attr_value = NULL;
 491     int score = not_set_value;
 492     pe_node_t *match = NULL;
 493 
 494     CRM_CHECK(node != NULL, return not_set_value);
 495 
 496     if (rsc->children) {
 497         GList *gIter = rsc->children;
 498 
 499         for (; gIter != NULL; gIter = gIter->next) {
 500             pe_resource_t *child = (pe_resource_t *) gIter->data;
 501             int c_score = promotion_score(child, node, not_set_value);
 502 
 503             if (score == not_set_value) {
 504                 score = c_score;
 505             } else {
 506                 score += c_score;
 507             }
 508         }
 509         return score;
 510     }
 511 
 512     if (!pcmk_is_set(rsc->flags, pe_rsc_unique)
 513         && filter_anonymous_instance(rsc, node)) {
 514 
 515         pe_rsc_trace(rsc, "Anonymous clone %s is allowed on %s", rsc->id, node->details->uname);
 516 
 517     } else if (rsc->running_on || g_hash_table_size(rsc->known_on)) {
 518         /* If we've probed and/or started the resource anywhere, consider
 519          * promotion scores only from nodes where we know the status. However,
 520          * if the status of all nodes is unknown (e.g. cluster startup),
 521          * skip this code, to make sure we take into account any permanent
 522          * promotion scores set previously.
 523          */
 524         pe_node_t *known = pe_hash_table_lookup(rsc->known_on, node->details->id);
 525 
 526         match = pe_find_node_id(rsc->running_on, node->details->id);
 527         if ((match == NULL) && (known == NULL)) {
 528             pe_rsc_trace(rsc, "skipping %s (aka. %s) promotion score on %s because inactive",
 529                          rsc->id, rsc->clone_name, node->details->uname);
 530             return score;
 531         }
 532     }
 533 
 534     match = pe_hash_table_lookup(rsc->allowed_nodes, node->details->id);
 535     if (match == NULL) {
 536         return score;
 537 
 538     } else if (match->weight < 0) {
 539         pe_rsc_trace(rsc, "%s on %s has score: %d - ignoring",
 540                      rsc->id, match->details->uname, match->weight);
 541         return score;
 542     }
 543 
 544     if (rsc->clone_name) {
 545         /* Use the name the lrm knows this resource as,
 546          * since that's what crm_attribute --promotion would have used
 547          */
 548         name = rsc->clone_name;
 549     }
 550 
 551     attr_value = lookup_promotion_score(rsc, node, name);
 552     pe_rsc_trace(rsc, "promotion score for %s on %s = %s",
 553                  name, node->details->uname, crm_str(attr_value));
 554 
 555     if ((attr_value == NULL) && !pcmk_is_set(rsc->flags, pe_rsc_unique)) {
 556         /* If we don't have any LRM history yet, we won't have clone_name -- in
 557          * that case, for anonymous clones, try the resource name without any
 558          * instance number.
 559          */
 560         name = clone_strip(rsc->id);
 561         if (strcmp(rsc->id, name)) {
 562             attr_value = lookup_promotion_score(rsc, node, name);
 563             pe_rsc_trace(rsc, "stripped promotion score for %s on %s = %s",
 564                          name, node->details->uname, crm_str(attr_value));
 565         }
 566         free(name);
 567     }
 568 
 569     if (attr_value != NULL) {
 570         score = char2score(attr_value);
 571     }
 572 
 573     return score;
 574 }
 575 
 576 void
 577 pcmk__add_promotion_scores(pe_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
 578 {
 579     int score, new_score;
 580     GList *gIter = rsc->children;
 581     clone_variant_data_t *clone_data = NULL;
 582 
 583     get_clone_variant_data(clone_data, rsc);
 584 
 585     if (clone_data->added_promotion_scores) {
 586         /* Make sure we only do this once */
 587         return;
 588     }
 589 
 590     clone_data->added_promotion_scores = true;
 591 
 592     for (; gIter != NULL; gIter = gIter->next) {
 593         GHashTableIter iter;
 594         pe_node_t *node = NULL;
 595         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 596 
 597         g_hash_table_iter_init(&iter, child_rsc->allowed_nodes);
 598         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 599             if (!pcmk__node_available(node)) {
 600                 /* This node will never be promoted, so don't apply the
 601                  * promotion score, as that may lead to clone shuffling.
 602                  */
 603                 continue;
 604             }
 605 
 606             score = promotion_score(child_rsc, node, 0);
 607             if (score > 0) {
 608                 new_score = pcmk__add_scores(node->weight, score);
 609                 if (new_score != node->weight) {
 610                     pe_rsc_trace(rsc, "\t%s: Updating preference for %s (%d->%d)",
 611                                  child_rsc->id, node->details->uname, node->weight, new_score);
 612                     node->weight = new_score;
 613                 }
 614             }
 615 
 616             new_score = QB_MAX(child_rsc->priority, score);
 617             if (new_score != child_rsc->priority) {
 618                 pe_rsc_trace(rsc, "\t%s: Updating priority (%d->%d)",
 619                              child_rsc->id, child_rsc->priority, new_score);
 620                 child_rsc->priority = new_score;
 621             }
 622         }
 623     }
 624 }
 625 
 626 static void
 627 set_role_unpromoted(pe_resource_t *rsc, bool current)
     /* [previous][next][first][last][top][bottom][index][help] */
 628 {
 629     GList *gIter = rsc->children;
 630 
 631     if (current) {
 632         if (rsc->role == RSC_ROLE_STARTED) {
 633             rsc->role = RSC_ROLE_UNPROMOTED;
 634         }
 635 
 636     } else {
 637         GList *allocated = NULL;
 638 
 639         rsc->fns->location(rsc, &allocated, FALSE);
 640         pe__set_next_role(rsc, (allocated? RSC_ROLE_UNPROMOTED : RSC_ROLE_STOPPED),
 641                           "unpromoted instance");
 642         g_list_free(allocated);
 643     }
 644 
 645     for (; gIter != NULL; gIter = gIter->next) {
 646         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 647 
 648         set_role_unpromoted(child_rsc, current);
 649     }
 650 }
 651 
 652 static void
 653 set_role_promoted(pe_resource_t *rsc, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
 654 {
 655     if (rsc->next_role == RSC_ROLE_UNKNOWN) {
 656         pe__set_next_role(rsc, RSC_ROLE_PROMOTED, "promoted instance");
 657     }
 658 
 659     g_list_foreach(rsc->children, (GFunc) set_role_promoted, NULL);
 660 }
 661 
 662 pe_node_t *
 663 pcmk__set_instance_roles(pe_resource_t *rsc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 664 {
 665     int promoted = 0;
 666     GList *gIter = NULL;
 667     GList *gIter2 = NULL;
 668     GHashTableIter iter;
 669     pe_node_t *node = NULL;
 670     pe_node_t *chosen = NULL;
 671     enum rsc_role_e next_role = RSC_ROLE_UNKNOWN;
 672     char score[33];
 673     size_t len = sizeof(score);
 674     clone_variant_data_t *clone_data = NULL;
 675 
 676     get_clone_variant_data(clone_data, rsc);
 677 
 678     // Repurpose count to track the number of promoted instances allocated
 679     g_hash_table_iter_init(&iter, rsc->allowed_nodes);
 680     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 681         node->count = 0;
 682     }
 683 
 684     /*
 685      * assign priority
 686      */
 687     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 688         GList *list = NULL;
 689         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 690 
 691         pe_rsc_trace(rsc, "Assigning priority for %s: %s", child_rsc->id,
 692                      role2text(child_rsc->next_role));
 693 
 694         if (child_rsc->fns->state(child_rsc, TRUE) == RSC_ROLE_STARTED) {
 695             set_role_unpromoted(child_rsc, true);
 696         }
 697 
 698         chosen = child_rsc->fns->location(child_rsc, &list, FALSE);
 699         if (pcmk__list_of_multiple(list)) {
 700             pcmk__config_err("Cannot promote non-colocated child %s",
 701                              child_rsc->id);
 702         }
 703 
 704         g_list_free(list);
 705         if (chosen == NULL) {
 706             continue;
 707         }
 708 
 709         next_role = child_rsc->fns->state(child_rsc, FALSE);
 710         switch (next_role) {
 711             case RSC_ROLE_STARTED:
 712             case RSC_ROLE_UNKNOWN:
 713                 /*
 714                  * Default to -1 if no value is set
 715                  *
 716                  * This allows instances eligible for promotion to be specified
 717                  * based solely on rsc_location constraints,
 718                  * but prevents anyone from being promoted if
 719                  * neither a constraint nor a promotion score is present
 720                  */
 721                 child_rsc->priority = promotion_score(child_rsc, chosen, -1);
 722                 break;
 723 
 724             case RSC_ROLE_UNPROMOTED:
 725             case RSC_ROLE_STOPPED:
 726                 child_rsc->priority = -INFINITY;
 727                 break;
 728             case RSC_ROLE_PROMOTED:
 729                 /* We will arrive here if we're re-creating actions after a stonith
 730                  */
 731                 break;
 732             default:
 733                 CRM_CHECK(FALSE /* unhandled */ ,
 734                           crm_err("Unknown resource role: %d for %s", next_role, child_rsc->id));
 735         }
 736 
 737         apply_promoted_location(child_rsc, child_rsc->rsc_location, chosen);
 738         apply_promoted_location(child_rsc, rsc->rsc_location, chosen);
 739 
 740         for (gIter2 = child_rsc->rsc_cons; gIter2 != NULL; gIter2 = gIter2->next) {
 741             pcmk__colocation_t *cons = (pcmk__colocation_t *) gIter2->data;
 742 
 743             child_rsc->cmds->rsc_colocation_lh(child_rsc, cons->primary, cons,
 744                                                data_set);
 745         }
 746 
 747         child_rsc->sort_index = child_rsc->priority;
 748         pe_rsc_trace(rsc, "Assigning priority for %s: %d", child_rsc->id, child_rsc->priority);
 749 
 750         if (next_role == RSC_ROLE_PROMOTED) {
 751             child_rsc->sort_index = INFINITY;
 752         }
 753     }
 754 
 755     pe__show_node_weights(true, rsc, "Pre merge", rsc->allowed_nodes, data_set);
 756     promotion_order(rsc, data_set);
 757 
 758     // Choose the first N eligible instances to be promoted
 759     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 760         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 761         score2char_stack(child_rsc->sort_index, score, len);
 762 
 763         chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
 764         if (pcmk_is_set(data_set->flags, pe_flag_show_scores) && !pcmk__is_daemon) {
 765             if (data_set->priv != NULL) {
 766                 pcmk__output_t *out = data_set->priv;
 767                 out->message(out, "promotion-score", child_rsc, chosen, score);
 768             }
 769 
 770         } else {
 771             pe_rsc_trace(rsc, "%s promotion score on %s: %s", child_rsc->id,
 772                          (chosen? chosen->details->uname : "none"), score);
 773         }
 774 
 775         chosen = NULL;          /* nuke 'chosen' so that we don't promote more than the
 776                                  * required number of instances
 777                                  */
 778 
 779         if (child_rsc->sort_index < 0) {
 780             pe_rsc_trace(rsc, "Not supposed to promote child: %s", child_rsc->id);
 781 
 782         } else if ((promoted < clone_data->promoted_max)
 783                    || !pcmk_is_set(rsc->flags, pe_rsc_managed)) {
 784             chosen = node_to_be_promoted_on(child_rsc);
 785         }
 786 
 787         pe_rsc_debug(rsc, "%s promotion score: %d", child_rsc->id, child_rsc->priority);
 788 
 789         if (chosen == NULL) {
 790             set_role_unpromoted(child_rsc, false);
 791             continue;
 792 
 793         } else if ((child_rsc->role < RSC_ROLE_PROMOTED)
 794               && !pcmk_is_set(data_set->flags, pe_flag_have_quorum)
 795               && data_set->no_quorum_policy == no_quorum_freeze) {
 796             crm_notice("Resource %s cannot be elevated from %s to %s: no-quorum-policy=freeze",
 797                        child_rsc->id, role2text(child_rsc->role), role2text(child_rsc->next_role));
 798             set_role_unpromoted(child_rsc, false);
 799             continue;
 800         }
 801 
 802         chosen->count++;
 803         pe_rsc_info(rsc, "Promoting %s (%s %s)",
 804                     child_rsc->id, role2text(child_rsc->role), chosen->details->uname);
 805         set_role_promoted(child_rsc, NULL);
 806         promoted++;
 807     }
 808 
 809     pe_rsc_info(rsc, "%s: Promoted %d instances of a possible %d",
 810                 rsc->id, promoted, clone_data->promoted_max);
 811 
 812     return NULL;
 813 }
 814 
 815 void
 816 create_promotable_actions(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 817 {
 818     pe_action_t *action = NULL;
 819     GList *gIter = rsc->children;
 820     pe_action_t *action_complete = NULL;
 821     gboolean any_promoting = FALSE;
 822     gboolean any_demoting = FALSE;
 823     pe_resource_t *last_promote_rsc = NULL;
 824     pe_resource_t *last_demote_rsc = NULL;
 825 
 826     clone_variant_data_t *clone_data = NULL;
 827 
 828     get_clone_variant_data(clone_data, rsc);
 829 
 830     pe_rsc_debug(rsc, "Creating actions for %s", rsc->id);
 831 
 832     for (; gIter != NULL; gIter = gIter->next) {
 833         gboolean child_promoting = FALSE;
 834         gboolean child_demoting = FALSE;
 835         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 836 
 837         pe_rsc_trace(rsc, "Creating actions for %s", child_rsc->id);
 838         child_rsc->cmds->create_actions(child_rsc, data_set);
 839         check_promotable_actions(child_rsc, &child_demoting, &child_promoting);
 840 
 841         any_demoting = any_demoting || child_demoting;
 842         any_promoting = any_promoting || child_promoting;
 843         pe_rsc_trace(rsc, "Created actions for %s: %d %d", child_rsc->id, child_promoting,
 844                      child_demoting);
 845     }
 846 
 847     /* promote */
 848     action = pcmk__new_rsc_pseudo_action(rsc, RSC_PROMOTE, !any_promoting,
 849                                          true);
 850     action_complete = pcmk__new_rsc_pseudo_action(rsc, RSC_PROMOTED,
 851                                                   !any_promoting, true);
 852     action_complete->priority = INFINITY;
 853 
 854     child_promoting_constraints(clone_data, pe_order_optional,
 855                                 rsc, NULL, last_promote_rsc, data_set);
 856 
 857     if (clone_data->promote_notify == NULL) {
 858         clone_data->promote_notify = pcmk__clone_notif_pseudo_ops(rsc,
 859                                                                   RSC_PROMOTE,
 860                                                                   action,
 861                                                                   action_complete);
 862     }
 863 
 864     /* demote */
 865     action = pcmk__new_rsc_pseudo_action(rsc, RSC_DEMOTE, !any_demoting, true);
 866     action_complete = pcmk__new_rsc_pseudo_action(rsc, RSC_DEMOTED,
 867                                                   !any_demoting, true);
 868     action_complete->priority = INFINITY;
 869 
 870     child_demoting_constraints(clone_data, pe_order_optional, rsc, NULL, last_demote_rsc, data_set);
 871 
 872     if (clone_data->demote_notify == NULL) {
 873         clone_data->demote_notify = pcmk__clone_notif_pseudo_ops(rsc,
 874                                                                  RSC_DEMOTE,
 875                                                                  action,
 876                                                                  action_complete);
 877 
 878         if (clone_data->promote_notify) {
 879             /* If we ever wanted groups to have notifications we'd need to move this to native_internal_constraints() one day
 880              * Requires exposing *_notify
 881              */
 882             order_actions(clone_data->stop_notify->post_done, clone_data->promote_notify->pre,
 883                           pe_order_optional);
 884             order_actions(clone_data->start_notify->post_done, clone_data->promote_notify->pre,
 885                           pe_order_optional);
 886             order_actions(clone_data->demote_notify->post_done, clone_data->promote_notify->pre,
 887                           pe_order_optional);
 888             order_actions(clone_data->demote_notify->post_done, clone_data->start_notify->pre,
 889                           pe_order_optional);
 890             order_actions(clone_data->demote_notify->post_done, clone_data->stop_notify->pre,
 891                           pe_order_optional);
 892         }
 893     }
 894 
 895     /* restore the correct priority */
 896 
 897     gIter = rsc->children;
 898     for (; gIter != NULL; gIter = gIter->next) {
 899         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 900 
 901         child_rsc->priority = rsc->priority;
 902     }
 903 }
 904 
 905 void
 906 promote_demote_constraints(pe_resource_t *rsc, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 907 {
 908     /* global stopped before start */
 909     pcmk__order_resource_actions(rsc, RSC_STOPPED, rsc, RSC_START,
 910                                  pe_order_optional, data_set);
 911 
 912     /* global stopped before promote */
 913     pcmk__order_resource_actions(rsc, RSC_STOPPED, rsc, RSC_PROMOTE,
 914                                  pe_order_optional, data_set);
 915 
 916     /* global demoted before start */
 917     pcmk__order_resource_actions(rsc, RSC_DEMOTED, rsc, RSC_START,
 918                                  pe_order_optional, data_set);
 919 
 920     /* global started before promote */
 921     pcmk__order_resource_actions(rsc, RSC_STARTED, rsc, RSC_PROMOTE,
 922                                  pe_order_optional, data_set);
 923 
 924     /* global demoted before stop */
 925     pcmk__order_resource_actions(rsc, RSC_DEMOTED, rsc, RSC_STOP,
 926                                  pe_order_optional, data_set);
 927 
 928     /* global demote before demoted */
 929     pcmk__order_resource_actions(rsc, RSC_DEMOTE, rsc, RSC_DEMOTED,
 930                                  pe_order_optional, data_set);
 931 
 932     /* global demoted before promote */
 933     pcmk__order_resource_actions(rsc, RSC_DEMOTED, rsc, RSC_PROMOTE,
 934                                  pe_order_optional, data_set);
 935 }
 936 
 937 
 938 void
 939 promotable_constraints(pe_resource_t * rsc, pe_working_set_t * data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 940 {
 941     GList *gIter = rsc->children;
 942     pe_resource_t *last_rsc = NULL;
 943     clone_variant_data_t *clone_data = NULL;
 944 
 945     get_clone_variant_data(clone_data, rsc);
 946 
 947     promote_demote_constraints(rsc, data_set);
 948 
 949     for (; gIter != NULL; gIter = gIter->next) {
 950         pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
 951 
 952         /* child demote before promote */
 953         pcmk__order_resource_actions(child_rsc, RSC_DEMOTE, child_rsc,
 954                                      RSC_PROMOTE, pe_order_optional, data_set);
 955 
 956         child_promoting_constraints(clone_data, pe_order_optional,
 957                                     rsc, child_rsc, last_rsc, data_set);
 958 
 959         child_demoting_constraints(clone_data, pe_order_optional,
 960                                    rsc, child_rsc, last_rsc, data_set);
 961 
 962         last_rsc = child_rsc;
 963     }
 964 }
 965 
 966 static void
 967 node_hash_update_one(GHashTable * hash, pe_node_t * other, const char *attr, int score)
     /* [previous][next][first][last][top][bottom][index][help] */
 968 {
 969     GHashTableIter iter;
 970     pe_node_t *node = NULL;
 971     const char *value = NULL;
 972 
 973     if (other == NULL) {
 974         return;
 975 
 976     } else if (attr == NULL) {
 977         attr = CRM_ATTR_UNAME;
 978     }
 979  
 980     value = pe_node_attribute_raw(other, attr);
 981     g_hash_table_iter_init(&iter, hash);
 982     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 983         const char *tmp = pe_node_attribute_raw(node, attr);
 984 
 985         if (pcmk__str_eq(value, tmp, pcmk__str_casei)) {
 986             crm_trace("%s: %d + %d", node->details->uname, node->weight, other->weight);
 987             node->weight = pcmk__add_scores(node->weight, score);
 988         }
 989     }
 990 }
 991 
 992 void
 993 promotable_colocation_rh(pe_resource_t *dependent, pe_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
 994                          pcmk__colocation_t *constraint,
 995                          pe_working_set_t *data_set)
 996 {
 997     GList *gIter = NULL;
 998 
 999     if (pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
1000         GList *affected_nodes = NULL;
1001 
1002         for (gIter = primary->children; gIter != NULL; gIter = gIter->next) {
1003             pe_resource_t *child_rsc = (pe_resource_t *) gIter->data;
1004             pe_node_t *chosen = child_rsc->fns->location(child_rsc, NULL, FALSE);
1005             enum rsc_role_e next_role = child_rsc->fns->state(child_rsc, FALSE);
1006 
1007             pe_rsc_trace(primary, "Processing: %s", child_rsc->id);
1008             if ((chosen != NULL) && (next_role == constraint->primary_role)) {
1009                 pe_rsc_trace(primary, "Applying: %s %s %s %d", child_rsc->id,
1010                              role2text(next_role), chosen->details->uname, constraint->score);
1011                 if (constraint->score < INFINITY) {
1012                     node_hash_update_one(dependent->allowed_nodes, chosen,
1013                                          constraint->node_attribute, constraint->score);
1014                 }
1015                 affected_nodes = g_list_prepend(affected_nodes, chosen);
1016             }
1017         }
1018 
1019         /* Only do this if it's not a promoted-with-promoted colocation. Doing
1020          * this unconditionally would prevent unpromoted instances from being
1021          * started.
1022          */
1023         if ((constraint->dependent_role != RSC_ROLE_PROMOTED)
1024             || (constraint->primary_role != RSC_ROLE_PROMOTED)) {
1025 
1026             if (constraint->score >= INFINITY) {
1027                 node_list_exclude(dependent->allowed_nodes, affected_nodes,
1028                                   TRUE);
1029             }
1030         }
1031         g_list_free(affected_nodes);
1032 
1033     } else if (constraint->dependent_role == RSC_ROLE_PROMOTED) {
1034         pe_resource_t *primary_instance;
1035 
1036         primary_instance = find_compatible_child(dependent, primary,
1037                                                  constraint->primary_role,
1038                                                  FALSE, data_set);
1039         if ((primary_instance == NULL) && (constraint->score >= INFINITY)) {
1040             pe_rsc_trace(dependent, "%s can't be promoted %s",
1041                          dependent->id, constraint->id);
1042             dependent->priority = -INFINITY;
1043 
1044         } else if (primary_instance != NULL) {
1045             int new_priority = pcmk__add_scores(dependent->priority,
1046                                                 constraint->score);
1047 
1048             pe_rsc_debug(dependent, "Applying %s to %s",
1049                          constraint->id, dependent->id);
1050             pe_rsc_debug(dependent, "\t%s: %d->%d",
1051                          dependent->id, dependent->priority, new_priority);
1052             dependent->priority = new_priority;
1053         }
1054     }
1055 
1056     return;
1057 }

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