root/lib/pacemaker/pcmk_sched_colocation.c

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

DEFINITIONS

This source file includes following definitions.
  1. cmp_dependent_priority
  2. cmp_primary_priority
  3. anti_colocation_order
  4. pcmk__new_colocation
  5. unpack_influence
  6. unpack_colocation_set
  7. colocate_rsc_sets
  8. unpack_simple_colocation
  9. unpack_colocation_tags
  10. pcmk__unpack_colocation
  11. mark_start_blocked
  12. pcmk__block_colocated_starts
  13. pcmk__colocation_affects
  14. pcmk__apply_coloc_to_weights
  15. pcmk__apply_coloc_to_priority

   1 /*
   2  * Copyright 2004-2021 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 <stdbool.h>
  13 #include <glib.h>
  14 
  15 #include <crm/crm.h>
  16 #include <crm/pengine/status.h>
  17 #include <pacemaker-internal.h>
  18 
  19 #include "libpacemaker_private.h"
  20 
  21 #define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do {                      \
  22         __rsc = pcmk__find_constraint_resource(data_set->resources, __name);    \
  23         if (__rsc == NULL) {                                                    \
  24             pcmk__config_err("%s: No resource found for %s", __set, __name);    \
  25             return;                                                             \
  26         }                                                                       \
  27     } while(0)
  28 
  29 static gint
  30 cmp_dependent_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
  31 {
  32     const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
  33     const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
  34 
  35     if (a == NULL) {
  36         return 1;
  37     }
  38     if (b == NULL) {
  39         return -1;
  40     }
  41 
  42     CRM_ASSERT(rsc_constraint1->dependent != NULL);
  43     CRM_ASSERT(rsc_constraint1->primary != NULL);
  44 
  45     if (rsc_constraint1->dependent->priority > rsc_constraint2->dependent->priority) {
  46         return -1;
  47     }
  48 
  49     if (rsc_constraint1->dependent->priority < rsc_constraint2->dependent->priority) {
  50         return 1;
  51     }
  52 
  53     /* Process clones before primitives and groups */
  54     if (rsc_constraint1->dependent->variant > rsc_constraint2->dependent->variant) {
  55         return -1;
  56     }
  57     if (rsc_constraint1->dependent->variant < rsc_constraint2->dependent->variant) {
  58         return 1;
  59     }
  60 
  61     /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
  62      * clones (probably unnecessary, but avoids having to update regression
  63      * tests)
  64      */
  65     if (rsc_constraint1->dependent->variant == pe_clone) {
  66         if (pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
  67             && !pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
  68             return -1;
  69         } else if (!pcmk_is_set(rsc_constraint1->dependent->flags, pe_rsc_promotable)
  70             && pcmk_is_set(rsc_constraint2->dependent->flags, pe_rsc_promotable)) {
  71             return 1;
  72         }
  73     }
  74 
  75     return strcmp(rsc_constraint1->dependent->id,
  76                   rsc_constraint2->dependent->id);
  77 }
  78 
  79 static gint
  80 cmp_primary_priority(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82     const pcmk__colocation_t *rsc_constraint1 = (const pcmk__colocation_t *) a;
  83     const pcmk__colocation_t *rsc_constraint2 = (const pcmk__colocation_t *) b;
  84 
  85     if (a == NULL) {
  86         return 1;
  87     }
  88     if (b == NULL) {
  89         return -1;
  90     }
  91 
  92     CRM_ASSERT(rsc_constraint1->dependent != NULL);
  93     CRM_ASSERT(rsc_constraint1->primary != NULL);
  94 
  95     if (rsc_constraint1->primary->priority > rsc_constraint2->primary->priority) {
  96         return -1;
  97     }
  98 
  99     if (rsc_constraint1->primary->priority < rsc_constraint2->primary->priority) {
 100         return 1;
 101     }
 102 
 103     /* Process clones before primitives and groups */
 104     if (rsc_constraint1->primary->variant > rsc_constraint2->primary->variant) {
 105         return -1;
 106     } else if (rsc_constraint1->primary->variant < rsc_constraint2->primary->variant) {
 107         return 1;
 108     }
 109 
 110     /* @COMPAT scheduler <2.0.0: Process promotable clones before nonpromotable
 111      * clones (probably unnecessary, but avoids having to update regression
 112      * tests)
 113      */
 114     if (rsc_constraint1->primary->variant == pe_clone) {
 115         if (pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
 116             && !pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
 117             return -1;
 118         } else if (!pcmk_is_set(rsc_constraint1->primary->flags, pe_rsc_promotable)
 119             && pcmk_is_set(rsc_constraint2->primary->flags, pe_rsc_promotable)) {
 120             return 1;
 121         }
 122     }
 123 
 124     return strcmp(rsc_constraint1->primary->id, rsc_constraint2->primary->id);
 125 }
 126 
 127 /*!
 128  * \internal
 129  * \brief Add orderings necessary for an anti-colocation constraint
 130  */
 131 static void
 132 anti_colocation_order(pe_resource_t *first_rsc, int first_role,
     /* [previous][next][first][last][top][bottom][index][help] */
 133                       pe_resource_t *then_rsc, int then_role,
 134                       pe_working_set_t *data_set)
 135 {
 136     const char *first_tasks[] = { NULL, NULL };
 137     const char *then_tasks[] = { NULL, NULL };
 138 
 139     /* Actions to make first_rsc lose first_role */
 140     if (first_role == RSC_ROLE_PROMOTED) {
 141         first_tasks[0] = CRMD_ACTION_DEMOTE;
 142 
 143     } else {
 144         first_tasks[0] = CRMD_ACTION_STOP;
 145 
 146         if (first_role == RSC_ROLE_UNPROMOTED) {
 147             first_tasks[1] = CRMD_ACTION_PROMOTE;
 148         }
 149     }
 150 
 151     /* Actions to make then_rsc gain then_role */
 152     if (then_role == RSC_ROLE_PROMOTED) {
 153         then_tasks[0] = CRMD_ACTION_PROMOTE;
 154 
 155     } else {
 156         then_tasks[0] = CRMD_ACTION_START;
 157 
 158         if (then_role == RSC_ROLE_UNPROMOTED) {
 159             then_tasks[1] = CRMD_ACTION_DEMOTE;
 160         }
 161     }
 162 
 163     for (int first_lpc = 0;
 164          (first_lpc <= 1) && (first_tasks[first_lpc] != NULL); first_lpc++) {
 165 
 166         for (int then_lpc = 0;
 167              (then_lpc <= 1) && (then_tasks[then_lpc] != NULL); then_lpc++) {
 168 
 169             pcmk__order_resource_actions(first_rsc, first_tasks[first_lpc],
 170                                          then_rsc, then_tasks[then_lpc],
 171                                          pe_order_anti_colocation, data_set);
 172         }
 173     }
 174 }
 175 
 176 /*!
 177  * \internal
 178  * \brief Add a new colocation constraint to a cluster working set
 179  *
 180  * \param[in] id              XML ID for this constraint
 181  * \param[in] node_attr       Colocate by this attribute (or NULL for #uname)
 182  * \param[in] score           Constraint score
 183  * \param[in] dependent       Resource to be colocated
 184  * \param[in] primary         Resource to colocate \p dependent with
 185  * \param[in] dependent_role  Current role of \p dependent
 186  * \param[in] primary_role    Current role of \p primary
 187  * \param[in] influence       Whether colocation constraint has influence
 188  * \param[in] data_set        Cluster working set to add constraint to
 189  */
 190 void
 191 pcmk__new_colocation(const char *id, const char *node_attr, int score,
     /* [previous][next][first][last][top][bottom][index][help] */
 192                      pe_resource_t *dependent, pe_resource_t *primary,
 193                      const char *dependent_role, const char *primary_role,
 194                      bool influence, pe_working_set_t *data_set)
 195 {
 196     pcmk__colocation_t *new_con = NULL;
 197 
 198     if (score == 0) {
 199         crm_trace("Ignoring colocation '%s' because score is 0", id);
 200         return;
 201     }
 202     if ((dependent == NULL) || (primary == NULL)) {
 203         pcmk__config_err("Ignoring colocation '%s' because resource "
 204                          "does not exist", id);
 205         return;
 206     }
 207 
 208     new_con = calloc(1, sizeof(pcmk__colocation_t));
 209     if (new_con == NULL) {
 210         return;
 211     }
 212 
 213     if (pcmk__str_eq(dependent_role, RSC_ROLE_STARTED_S,
 214                      pcmk__str_null_matches|pcmk__str_casei)) {
 215         dependent_role = RSC_ROLE_UNKNOWN_S;
 216     }
 217 
 218     if (pcmk__str_eq(primary_role, RSC_ROLE_STARTED_S,
 219                      pcmk__str_null_matches|pcmk__str_casei)) {
 220         primary_role = RSC_ROLE_UNKNOWN_S;
 221     }
 222 
 223     new_con->id = id;
 224     new_con->dependent = dependent;
 225     new_con->primary = primary;
 226     new_con->score = score;
 227     new_con->dependent_role = text2role(dependent_role);
 228     new_con->primary_role = text2role(primary_role);
 229     new_con->node_attribute = node_attr;
 230     new_con->influence = influence;
 231 
 232     if (node_attr == NULL) {
 233         node_attr = CRM_ATTR_UNAME;
 234     }
 235 
 236     pe_rsc_trace(dependent, "%s ==> %s (%s %d)",
 237                  dependent->id, primary->id, node_attr, score);
 238 
 239     dependent->rsc_cons = g_list_insert_sorted(dependent->rsc_cons, new_con,
 240                                                cmp_primary_priority);
 241 
 242     primary->rsc_cons_lhs = g_list_insert_sorted(primary->rsc_cons_lhs, new_con,
 243                                                  cmp_dependent_priority);
 244 
 245     data_set->colocation_constraints = g_list_append(data_set->colocation_constraints,
 246                                                      new_con);
 247 
 248     if (score <= -INFINITY) {
 249         anti_colocation_order(dependent, new_con->dependent_role, primary,
 250                               new_con->primary_role, data_set);
 251         anti_colocation_order(primary, new_con->primary_role, dependent,
 252                               new_con->dependent_role, data_set);
 253     }
 254 }
 255 
 256 /*!
 257  * \internal
 258  * \brief Return the boolean influence corresponding to configuration
 259  *
 260  * \param[in] coloc_id     Colocation XML ID (for error logging)
 261  * \param[in] rsc          Resource involved in constraint (for default)
 262  * \param[in] influence_s  String value of influence option
 263  *
 264  * \return true if string evaluates true, false if string evaluates false,
 265  *         or value of resource's critical option if string is NULL or invalid
 266  */
 267 static bool
 268 unpack_influence(const char *coloc_id, const pe_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
 269                  const char *influence_s)
 270 {
 271     if (influence_s != NULL) {
 272         int influence_i = 0;
 273 
 274         if (crm_str_to_boolean(influence_s, &influence_i) < 0) {
 275             pcmk__config_err("Constraint '%s' has invalid value for "
 276                              XML_COLOC_ATTR_INFLUENCE " (using default)",
 277                              coloc_id);
 278         } else {
 279             return (influence_i != 0);
 280         }
 281     }
 282     return pcmk_is_set(rsc->flags, pe_rsc_critical);
 283 }
 284 
 285 static void
 286 unpack_colocation_set(xmlNode *set, int score, const char *coloc_id,
     /* [previous][next][first][last][top][bottom][index][help] */
 287                       const char *influence_s, pe_working_set_t *data_set)
 288 {
 289     xmlNode *xml_rsc = NULL;
 290     pe_resource_t *with = NULL;
 291     pe_resource_t *resource = NULL;
 292     const char *set_id = ID(set);
 293     const char *role = crm_element_value(set, "role");
 294     const char *sequential = crm_element_value(set, "sequential");
 295     const char *ordering = crm_element_value(set, "ordering");
 296     int local_score = score;
 297 
 298     const char *score_s = crm_element_value(set, XML_RULE_ATTR_SCORE);
 299 
 300     if (score_s) {
 301         local_score = char2score(score_s);
 302     }
 303     if (local_score == 0) {
 304         crm_trace("Ignoring colocation '%s' for set '%s' because score is 0",
 305                   coloc_id, set_id);
 306         return;
 307     }
 308 
 309     if (ordering == NULL) {
 310         ordering = "group";
 311     }
 312 
 313     if ((sequential != NULL) && !crm_is_true(sequential)) {
 314         return;
 315 
 316     } else if ((local_score > 0)
 317                && pcmk__str_eq(ordering, "group", pcmk__str_casei)) {
 318         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 319              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 320 
 321             EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
 322             if (with != NULL) {
 323                 pe_rsc_trace(resource, "Colocating %s with %s", resource->id, with->id);
 324                 pcmk__new_colocation(set_id, NULL, local_score, resource,
 325                                      with, role, role,
 326                                      unpack_influence(coloc_id, resource,
 327                                                       influence_s), data_set);
 328             }
 329             with = resource;
 330         }
 331 
 332     } else if (local_score > 0) {
 333         pe_resource_t *last = NULL;
 334 
 335         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 336              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 337 
 338             EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
 339             if (last != NULL) {
 340                 pe_rsc_trace(resource, "Colocating %s with %s",
 341                              last->id, resource->id);
 342                 pcmk__new_colocation(set_id, NULL, local_score, last,
 343                                      resource, role, role,
 344                                      unpack_influence(coloc_id, last,
 345                                                       influence_s), data_set);
 346             }
 347 
 348             last = resource;
 349         }
 350 
 351     } else {
 352         /* Anti-colocating with every prior resource is
 353          * the only way to ensure the intuitive result
 354          * (i.e. that no one in the set can run with anyone else in the set)
 355          */
 356 
 357         for (xml_rsc = first_named_child(set, XML_TAG_RESOURCE_REF);
 358              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 359 
 360             xmlNode *xml_rsc_with = NULL;
 361             bool influence = true;
 362 
 363             EXPAND_CONSTRAINT_IDREF(set_id, resource, ID(xml_rsc));
 364             influence = unpack_influence(coloc_id, resource, influence_s);
 365 
 366             for (xml_rsc_with = first_named_child(set, XML_TAG_RESOURCE_REF);
 367                  xml_rsc_with != NULL;
 368                  xml_rsc_with = crm_next_same_xml(xml_rsc_with)) {
 369 
 370                 if (pcmk__str_eq(resource->id, ID(xml_rsc_with),
 371                                  pcmk__str_casei)) {
 372                     break;
 373                 }
 374                 EXPAND_CONSTRAINT_IDREF(set_id, with, ID(xml_rsc_with));
 375                 pe_rsc_trace(resource, "Anti-Colocating %s with %s", resource->id,
 376                              with->id);
 377                 pcmk__new_colocation(set_id, NULL, local_score,
 378                                      resource, with, role, role,
 379                                      influence, data_set);
 380             }
 381         }
 382     }
 383 }
 384 
 385 static void
 386 colocate_rsc_sets(const char *id, xmlNode *set1, xmlNode *set2, int score,
     /* [previous][next][first][last][top][bottom][index][help] */
 387                   const char *influence_s, pe_working_set_t *data_set)
 388 {
 389     xmlNode *xml_rsc = NULL;
 390     pe_resource_t *rsc_1 = NULL;
 391     pe_resource_t *rsc_2 = NULL;
 392 
 393     const char *role_1 = crm_element_value(set1, "role");
 394     const char *role_2 = crm_element_value(set2, "role");
 395 
 396     const char *sequential_1 = crm_element_value(set1, "sequential");
 397     const char *sequential_2 = crm_element_value(set2, "sequential");
 398 
 399     if (score == 0) {
 400         crm_trace("Ignoring colocation '%s' between sets because score is 0",
 401                   id);
 402         return;
 403     }
 404     if ((sequential_1 == NULL) || crm_is_true(sequential_1)) {
 405         // Get the first one
 406         xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 407         if (xml_rsc != NULL) {
 408             EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
 409         }
 410     }
 411 
 412     if ((sequential_2 == NULL) || crm_is_true(sequential_2)) {
 413         // Get the last one
 414         const char *rid = NULL;
 415 
 416         for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
 417              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 418 
 419             rid = ID(xml_rsc);
 420         }
 421         EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
 422     }
 423 
 424     if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
 425         pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1, role_2,
 426                              unpack_influence(id, rsc_1, influence_s),
 427                              data_set);
 428 
 429     } else if (rsc_1 != NULL) {
 430         bool influence = unpack_influence(id, rsc_1, influence_s);
 431 
 432         for (xml_rsc = first_named_child(set2, XML_TAG_RESOURCE_REF);
 433              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 434 
 435             EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc));
 436             pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
 437                                  role_2, influence, data_set);
 438         }
 439 
 440     } else if (rsc_2 != NULL) {
 441         for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 442              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 443 
 444             EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
 445             pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2, role_1,
 446                                  role_2,
 447                                  unpack_influence(id, rsc_1, influence_s),
 448                                  data_set);
 449         }
 450 
 451     } else {
 452         for (xml_rsc = first_named_child(set1, XML_TAG_RESOURCE_REF);
 453              xml_rsc != NULL; xml_rsc = crm_next_same_xml(xml_rsc)) {
 454 
 455             xmlNode *xml_rsc_2 = NULL;
 456             bool influence = true;
 457 
 458             EXPAND_CONSTRAINT_IDREF(id, rsc_1, ID(xml_rsc));
 459             influence = unpack_influence(id, rsc_1, influence_s);
 460 
 461             for (xml_rsc_2 = first_named_child(set2, XML_TAG_RESOURCE_REF);
 462                  xml_rsc_2 != NULL;
 463                  xml_rsc_2 = crm_next_same_xml(xml_rsc_2)) {
 464 
 465                 EXPAND_CONSTRAINT_IDREF(id, rsc_2, ID(xml_rsc_2));
 466                 pcmk__new_colocation(id, NULL, score, rsc_1, rsc_2,
 467                                      role_1, role_2, influence,
 468                                      data_set);
 469             }
 470         }
 471     }
 472 }
 473 
 474 static void
 475 unpack_simple_colocation(xmlNode *xml_obj, const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 476                          const char *influence_s, pe_working_set_t *data_set)
 477 {
 478     int score_i = 0;
 479 
 480     const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 481     const char *dependent_id = crm_element_value(xml_obj,
 482                                                  XML_COLOC_ATTR_SOURCE);
 483     const char *primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
 484     const char *dependent_role = crm_element_value(xml_obj,
 485                                                    XML_COLOC_ATTR_SOURCE_ROLE);
 486     const char *primary_role = crm_element_value(xml_obj,
 487                                                  XML_COLOC_ATTR_TARGET_ROLE);
 488     const char *attr = crm_element_value(xml_obj, XML_COLOC_ATTR_NODE_ATTR);
 489     const char *symmetrical = crm_element_value(xml_obj, XML_CONS_ATTR_SYMMETRICAL);
 490 
 491     // experimental syntax from pacemaker-next (unlikely to be adopted as-is)
 492     const char *dependent_instance = crm_element_value(xml_obj,
 493                                                        XML_COLOC_ATTR_SOURCE_INSTANCE);
 494     const char *primary_instance = crm_element_value(xml_obj,
 495                                                      XML_COLOC_ATTR_TARGET_INSTANCE);
 496 
 497     pe_resource_t *dependent = pcmk__find_constraint_resource(data_set->resources,
 498                                                               dependent_id);
 499     pe_resource_t *primary = pcmk__find_constraint_resource(data_set->resources,
 500                                                             primary_id);
 501 
 502     if (dependent == NULL) {
 503         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 504                          "does not exist", id, dependent_id);
 505         return;
 506 
 507     } else if (primary == NULL) {
 508         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 509                          "does not exist", id, primary_id);
 510         return;
 511 
 512     } else if ((dependent_instance != NULL) && !pe_rsc_is_clone(dependent)) {
 513         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 514                          "is not a clone but instance '%s' was requested",
 515                          id, dependent_id, dependent_instance);
 516         return;
 517 
 518     } else if ((primary_instance != NULL) && !pe_rsc_is_clone(primary)) {
 519         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 520                          "is not a clone but instance '%s' was requested",
 521                          id, primary_id, primary_instance);
 522         return;
 523     }
 524 
 525     if (dependent_instance != NULL) {
 526         dependent = find_clone_instance(dependent, dependent_instance, data_set);
 527         if (dependent == NULL) {
 528             pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 529                               "does not have an instance '%s'",
 530                               id, dependent_id, dependent_instance);
 531             return;
 532         }
 533     }
 534 
 535     if (primary_instance != NULL) {
 536         primary = find_clone_instance(primary, primary_instance, data_set);
 537         if (primary == NULL) {
 538             pcmk__config_warn("Ignoring constraint '%s' because resource '%s' "
 539                               "does not have an instance '%s'",
 540                               "'%s'", id, primary_id, primary_instance);
 541             return;
 542         }
 543     }
 544 
 545     if (crm_is_true(symmetrical)) {
 546         pcmk__config_warn("The colocation constraint '"
 547                           XML_CONS_ATTR_SYMMETRICAL
 548                           "' attribute has been removed");
 549     }
 550 
 551     if (score) {
 552         score_i = char2score(score);
 553     }
 554 
 555     pcmk__new_colocation(id, attr, score_i, dependent, primary,
 556                          dependent_role, primary_role,
 557                          unpack_influence(id, dependent, influence_s), data_set);
 558 }
 559 
 560 // \return Standard Pacemaker return code
 561 static int
 562 unpack_colocation_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 563                        pe_working_set_t *data_set)
 564 {
 565     const char *id = NULL;
 566     const char *dependent_id = NULL;
 567     const char *primary_id = NULL;
 568     const char *dependent_role = NULL;
 569     const char *primary_role = NULL;
 570 
 571     pe_resource_t *dependent = NULL;
 572     pe_resource_t *primary = NULL;
 573 
 574     pe_tag_t *dependent_tag = NULL;
 575     pe_tag_t *primary_tag = NULL;
 576 
 577     xmlNode *dependent_set = NULL;
 578     xmlNode *primary_set = NULL;
 579     bool any_sets = false;
 580 
 581     *expanded_xml = NULL;
 582 
 583     CRM_CHECK(xml_obj != NULL, return pcmk_rc_schema_validation);
 584 
 585     id = ID(xml_obj);
 586     if (id == NULL) {
 587         pcmk__config_err("Ignoring <%s> constraint without " XML_ATTR_ID,
 588                          crm_element_name(xml_obj));
 589         return pcmk_rc_schema_validation;
 590     }
 591 
 592     // Check whether there are any resource sets with template or tag references
 593     *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, data_set);
 594     if (*expanded_xml != NULL) {
 595         crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
 596         return pcmk_rc_ok;
 597     }
 598 
 599     dependent_id = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE);
 600     primary_id = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET);
 601     if ((dependent_id == NULL) || (primary_id == NULL)) {
 602         return pcmk_rc_ok;
 603     }
 604 
 605     if (!pcmk__valid_resource_or_tag(data_set, dependent_id, &dependent,
 606                                      &dependent_tag)) {
 607         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 608                          "valid resource or tag", id, dependent_id);
 609         return pcmk_rc_schema_validation;
 610     }
 611 
 612     if (!pcmk__valid_resource_or_tag(data_set, primary_id, &primary,
 613                                      &primary_tag)) {
 614         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 615                          "valid resource or tag", id, primary_id);
 616         return pcmk_rc_schema_validation;
 617     }
 618 
 619     if ((dependent != NULL) && (primary != NULL)) {
 620         /* Neither side references any template/tag. */
 621         return pcmk_rc_ok;
 622     }
 623 
 624     if ((dependent_tag != NULL) && (primary_tag != NULL)) {
 625         // A colocation constraint between two templates/tags makes no sense
 626         pcmk__config_err("Ignoring constraint '%s' because two templates or "
 627                          "tags cannot be colocated", id);
 628         return pcmk_rc_schema_validation;
 629     }
 630 
 631     dependent_role = crm_element_value(xml_obj, XML_COLOC_ATTR_SOURCE_ROLE);
 632     primary_role = crm_element_value(xml_obj, XML_COLOC_ATTR_TARGET_ROLE);
 633 
 634     *expanded_xml = copy_xml(xml_obj);
 635 
 636     // Convert template/tag reference in "rsc" into resource_set under constraint
 637     if (!pcmk__tag_to_set(*expanded_xml, &dependent_set, XML_COLOC_ATTR_SOURCE,
 638                           true, data_set)) {
 639         free_xml(*expanded_xml);
 640         *expanded_xml = NULL;
 641         return pcmk_rc_schema_validation;
 642     }
 643 
 644     if (dependent_set != NULL) {
 645         if (dependent_role != NULL) {
 646             // Move "rsc-role" into converted resource_set as "role"
 647             crm_xml_add(dependent_set, "role", dependent_role);
 648             xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_SOURCE_ROLE);
 649         }
 650         any_sets = true;
 651     }
 652 
 653     // Convert template/tag reference in "with-rsc" into resource_set under constraint
 654     if (!pcmk__tag_to_set(*expanded_xml, &primary_set, XML_COLOC_ATTR_TARGET,
 655                           true, data_set)) {
 656         free_xml(*expanded_xml);
 657         *expanded_xml = NULL;
 658         return pcmk_rc_schema_validation;
 659     }
 660 
 661     if (primary_set != NULL) {
 662         if (primary_role != NULL) {
 663             // Move "with-rsc-role" into converted resource_set as "role"
 664             crm_xml_add(primary_set, "role", primary_role);
 665             xml_remove_prop(*expanded_xml, XML_COLOC_ATTR_TARGET_ROLE);
 666         }
 667         any_sets = true;
 668     }
 669 
 670     if (any_sets) {
 671         crm_log_xml_trace(*expanded_xml, "Expanded rsc_colocation");
 672     } else {
 673         free_xml(*expanded_xml);
 674         *expanded_xml = NULL;
 675     }
 676 
 677     return pcmk_rc_ok;
 678 }
 679 
 680 /*!
 681  * \internal
 682  * \brief Parse a colocation constraint from XML into a cluster working set
 683  *
 684  * \param[in] xml_obj   Colocation constraint XML to unpack
 685  * \param[in] data_set  Cluster working set to add constraint to
 686  */
 687 void
 688 pcmk__unpack_colocation(xmlNode *xml_obj, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 689 {
 690     int score_i = 0;
 691     xmlNode *set = NULL;
 692     xmlNode *last = NULL;
 693 
 694     xmlNode *orig_xml = NULL;
 695     xmlNode *expanded_xml = NULL;
 696 
 697     const char *id = crm_element_value(xml_obj, XML_ATTR_ID);
 698     const char *score = crm_element_value(xml_obj, XML_RULE_ATTR_SCORE);
 699     const char *influence_s = crm_element_value(xml_obj,
 700                                                 XML_COLOC_ATTR_INFLUENCE);
 701 
 702     if (score) {
 703         score_i = char2score(score);
 704     }
 705 
 706     if (unpack_colocation_tags(xml_obj, &expanded_xml,
 707                                data_set) != pcmk_rc_ok) {
 708         return;
 709     }
 710     if (expanded_xml) {
 711         orig_xml = xml_obj;
 712         xml_obj = expanded_xml;
 713     }
 714 
 715     for (set = first_named_child(xml_obj, XML_CONS_TAG_RSC_SET); set != NULL;
 716          set = crm_next_same_xml(set)) {
 717 
 718         set = expand_idref(set, data_set->input);
 719         if (set == NULL) { // Configuration error, message already logged
 720             if (expanded_xml != NULL) {
 721                 free_xml(expanded_xml);
 722             }
 723             return;
 724         }
 725 
 726         unpack_colocation_set(set, score_i, id, influence_s, data_set);
 727 
 728         if (last != NULL) {
 729             colocate_rsc_sets(id, last, set, score_i, influence_s, data_set);
 730         }
 731         last = set;
 732     }
 733 
 734     if (expanded_xml) {
 735         free_xml(expanded_xml);
 736         xml_obj = orig_xml;
 737     }
 738 
 739     if (last == NULL) {
 740         unpack_simple_colocation(xml_obj, id, influence_s, data_set);
 741     }
 742 }
 743 
 744 static void
 745 mark_start_blocked(pe_resource_t *rsc, pe_resource_t *reason,
     /* [previous][next][first][last][top][bottom][index][help] */
 746                    pe_working_set_t *data_set)
 747 {
 748     char *reason_text = crm_strdup_printf("colocation with %s", reason->id);
 749 
 750     for (GList *gIter = rsc->actions; gIter != NULL; gIter = gIter->next) {
 751         pe_action_t *action = (pe_action_t *) gIter->data;
 752 
 753         if (pcmk_is_set(action->flags, pe_action_runnable)
 754             && pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)) {
 755 
 756             pe__clear_action_flags(action, pe_action_runnable);
 757             pe_action_set_reason(action, reason_text, false);
 758             pcmk__block_colocated_starts(action, data_set);
 759             update_action(action, data_set);
 760         }
 761     }
 762     free(reason_text);
 763 }
 764 
 765 /*!
 766  * \internal
 767  * \brief If a start action is unrunnable, block starts of colocated resources
 768  *
 769  * \param[in] action    Action to check
 770  * \param[in] data_set  Cluster working set
 771  */
 772 void
 773 pcmk__block_colocated_starts(pe_action_t *action, pe_working_set_t *data_set)
     /* [previous][next][first][last][top][bottom][index][help] */
 774 {
 775     GList *gIter = NULL;
 776     pe_resource_t *rsc = NULL;
 777 
 778     if (!pcmk_is_set(action->flags, pe_action_runnable)
 779         && pcmk__str_eq(action->task, RSC_START, pcmk__str_casei)) {
 780 
 781         rsc = uber_parent(action->rsc);
 782         if (rsc->parent) {
 783             /* For bundles, uber_parent() returns the clone, not the bundle, so
 784              * the existence of rsc->parent implies this is a bundle.
 785              * In this case, we need the bundle resource, so that we can check
 786              * if all containers are stopped/stopping.
 787              */
 788             rsc = rsc->parent;
 789         }
 790     }
 791 
 792     if ((rsc == NULL) || (rsc->rsc_cons_lhs == NULL)) {
 793         return;
 794     }
 795 
 796     // Block colocated starts only if all children (if any) have unrunnable starts
 797     for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
 798         pe_resource_t *child = (pe_resource_t *)gIter->data;
 799         pe_action_t *start = find_first_action(child->actions, NULL, RSC_START, NULL);
 800 
 801         if ((start == NULL) || pcmk_is_set(start->flags, pe_action_runnable)) {
 802             return;
 803         }
 804     }
 805 
 806     for (gIter = rsc->rsc_cons_lhs; gIter != NULL; gIter = gIter->next) {
 807         pcmk__colocation_t *colocate_with = (pcmk__colocation_t *) gIter->data;
 808 
 809         if (colocate_with->score == INFINITY) {
 810             mark_start_blocked(colocate_with->dependent, action->rsc, data_set);
 811         }
 812     }
 813 }
 814 
 815 /*!
 816  * \internal
 817  * \brief Determine how a colocation constraint should affect a resource
 818  *
 819  * Colocation constraints have different effects at different points in the
 820  * scheduler sequence. Initially, they affect a resource's location; once that
 821  * is determined, then for promotable clones they can affect a resource
 822  * instance's role; after both are determined, the constraints no longer matter.
 823  * Given a specific colocation constraint, check what has been done so far to
 824  * determine what should be affected at the current point in the scheduler.
 825  *
 826  * \param[in] dependent   Dependent resource in colocation
 827  * \param[in] primary     Primary resource in colocation
 828  * \param[in] constraint  Colocation constraint
 829  * \param[in] preview     If true, pretend resources have already been allocated
 830  *
 831  * \return How colocation constraint should be applied at this point
 832  */
 833 enum pcmk__coloc_affects
 834 pcmk__colocation_affects(pe_resource_t *dependent, pe_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
 835                          pcmk__colocation_t *constraint, bool preview)
 836 {
 837     if (!preview && pcmk_is_set(primary->flags, pe_rsc_provisional)) {
 838         // Primary resource has not been allocated yet, so we can't do anything
 839         return pcmk__coloc_affects_nothing;
 840     }
 841 
 842     if ((constraint->dependent_role >= RSC_ROLE_UNPROMOTED)
 843         && (dependent->parent != NULL)
 844         && pcmk_is_set(dependent->parent->flags, pe_rsc_promotable)
 845         && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
 846 
 847         /* This is a colocation by role, and the dependent is a promotable clone
 848          * that has already been allocated, so the colocation should now affect
 849          * the role.
 850          */
 851         return pcmk__coloc_affects_role;
 852     }
 853 
 854     if (!preview && !pcmk_is_set(dependent->flags, pe_rsc_provisional)) {
 855         /* The dependent resource has already been through allocation, so the
 856          * constraint no longer has any effect. Log an error if a mandatory
 857          * colocation constraint has been violated.
 858          */
 859 
 860         const pe_node_t *primary_node = primary->allocated_to;
 861 
 862         if (dependent->allocated_to == NULL) {
 863             crm_trace("Skipping colocation '%s': %s will not run anywhere",
 864                       constraint->id, dependent->id);
 865 
 866         } else if (constraint->score >= INFINITY) {
 867             // Dependent resource must colocate with primary resource
 868 
 869             if ((primary_node == NULL) ||
 870                 (primary_node->details != dependent->allocated_to->details)) {
 871                 crm_err("%s must be colocated with %s but is not (%s vs. %s)",
 872                         dependent->id, primary->id,
 873                         dependent->allocated_to->details->uname,
 874                         (primary_node == NULL)? "unallocated" : primary_node->details->uname);
 875             }
 876 
 877         } else if (constraint->score <= -CRM_SCORE_INFINITY) {
 878             // Dependent resource must anti-colocate with primary resource
 879 
 880             if ((primary_node != NULL) &&
 881                 (dependent->allocated_to->details == primary_node->details)) {
 882                 crm_err("%s and %s must be anti-colocated but are allocated "
 883                         "to the same node (%s)",
 884                         dependent->id, primary->id, primary_node->details->uname);
 885             }
 886         }
 887         return pcmk__coloc_affects_nothing;
 888     }
 889 
 890     if ((constraint->score > 0)
 891         && (constraint->dependent_role != RSC_ROLE_UNKNOWN)
 892         && (constraint->dependent_role != dependent->next_role)) {
 893 
 894         crm_trace("Skipping colocation '%s': dependent limited to %s role "
 895                   "but %s next role is %s",
 896                   constraint->id, role2text(constraint->dependent_role),
 897                   dependent->id, role2text(dependent->next_role));
 898         return pcmk__coloc_affects_nothing;
 899     }
 900 
 901     if ((constraint->score > 0)
 902         && (constraint->primary_role != RSC_ROLE_UNKNOWN)
 903         && (constraint->primary_role != primary->next_role)) {
 904 
 905         crm_trace("Skipping colocation '%s': primary limited to %s role "
 906                   "but %s next role is %s",
 907                   constraint->id, role2text(constraint->primary_role),
 908                   primary->id, role2text(primary->next_role));
 909         return pcmk__coloc_affects_nothing;
 910     }
 911 
 912     if ((constraint->score < 0)
 913         && (constraint->dependent_role != RSC_ROLE_UNKNOWN)
 914         && (constraint->dependent_role == dependent->next_role)) {
 915         crm_trace("Skipping anti-colocation '%s': dependent role %s matches",
 916                   constraint->id, role2text(constraint->dependent_role));
 917         return pcmk__coloc_affects_nothing;
 918     }
 919 
 920     if ((constraint->score < 0)
 921         && (constraint->primary_role != RSC_ROLE_UNKNOWN)
 922         && (constraint->primary_role == primary->next_role)) {
 923         crm_trace("Skipping anti-colocation '%s': primary role %s matches",
 924                   constraint->id, role2text(constraint->primary_role));
 925         return pcmk__coloc_affects_nothing;
 926     }
 927 
 928     return pcmk__coloc_affects_location;
 929 }
 930 
 931 /*!
 932  * \internal
 933  * \brief Apply colocation to dependent for allocation purposes
 934  *
 935  * Update the allocated node weights of the dependent resource in a colocation,
 936  * for the purposes of allocating it to a node
 937  *
 938  * \param[in] dependent   Dependent resource in colocation
 939  * \param[in] primary     Primary resource in colocation
 940  * \param[in] constraint  Colocation constraint
 941  */
 942 void
 943 pcmk__apply_coloc_to_weights(pe_resource_t *dependent, pe_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
 944                              pcmk__colocation_t *constraint)
 945 {
 946     const char *attribute = CRM_ATTR_ID;
 947     const char *value = NULL;
 948     GHashTable *work = NULL;
 949     GHashTableIter iter;
 950     pe_node_t *node = NULL;
 951 
 952     if (constraint->node_attribute != NULL) {
 953         attribute = constraint->node_attribute;
 954     }
 955 
 956     if (primary->allocated_to != NULL) {
 957         value = pe_node_attribute_raw(primary->allocated_to, attribute);
 958 
 959     } else if (constraint->score < 0) {
 960         // Nothing to do (anti-colocation with something that is not running)
 961         return;
 962     }
 963 
 964     work = pcmk__copy_node_table(dependent->allowed_nodes);
 965 
 966     g_hash_table_iter_init(&iter, work);
 967     while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
 968         if (primary->allocated_to == NULL) {
 969             pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s inactive)",
 970                          constraint->id, dependent->id, node->details->uname,
 971                          constraint->score, primary->id);
 972             node->weight = pe__add_scores(-constraint->score, node->weight);
 973 
 974         } else if (pcmk__str_eq(pe_node_attribute_raw(node, attribute), value,
 975                                 pcmk__str_casei)) {
 976             if (constraint->score < CRM_SCORE_INFINITY) {
 977                 pe_rsc_trace(dependent, "%s: %s@%s += %d",
 978                              constraint->id, dependent->id,
 979                              node->details->uname, constraint->score);
 980                 node->weight = pe__add_scores(constraint->score, node->weight);
 981             }
 982 
 983         } else if (constraint->score >= CRM_SCORE_INFINITY) {
 984             pe_rsc_trace(dependent, "%s: %s@%s -= %d (%s mismatch)",
 985                          constraint->id, dependent->id, node->details->uname,
 986                          constraint->score, attribute);
 987             node->weight = pe__add_scores(-constraint->score, node->weight);
 988         }
 989     }
 990 
 991     if (can_run_any(work) || (constraint->score <= -INFINITY)
 992         || (constraint->score >= INFINITY)) {
 993 
 994         g_hash_table_destroy(dependent->allowed_nodes);
 995         dependent->allowed_nodes = work;
 996         work = NULL;
 997 
 998     } else {
 999         pe_rsc_info(dependent,
1000                     "%s: Rolling back scores from %s (no available nodes)",
1001                     dependent->id, primary->id);
1002     }
1003 
1004     if (work != NULL) {
1005         g_hash_table_destroy(work);
1006     }
1007 }
1008 
1009 /*!
1010  * \internal
1011  * \brief Apply colocation to dependent for role purposes
1012  *
1013  * Update the priority of the dependent resource in a colocation, for the
1014  * purposes of selecting its role
1015  *
1016  * \param[in] dependent   Dependent resource in colocation
1017  * \param[in] primary     Primary resource in colocation
1018  * \param[in] constraint  Colocation constraint
1019  */
1020 void
1021 pcmk__apply_coloc_to_priority(pe_resource_t *dependent, pe_resource_t *primary,
     /* [previous][next][first][last][top][bottom][index][help] */
1022                               pcmk__colocation_t *constraint)
1023 {
1024     const char *dependent_value = NULL;
1025     const char *primary_value = NULL;
1026     const char *attribute = CRM_ATTR_ID;
1027     int score_multiplier = 1;
1028 
1029     if ((primary->allocated_to == NULL) || (dependent->allocated_to == NULL)) {
1030         return;
1031     }
1032 
1033     if (constraint->node_attribute != NULL) {
1034         attribute = constraint->node_attribute;
1035     }
1036 
1037     dependent_value = pe_node_attribute_raw(dependent->allocated_to, attribute);
1038     primary_value = pe_node_attribute_raw(primary->allocated_to, attribute);
1039 
1040     if (!pcmk__str_eq(dependent_value, primary_value, pcmk__str_casei)) {
1041         if ((constraint->score == INFINITY)
1042             && (constraint->dependent_role == RSC_ROLE_PROMOTED)) {
1043             dependent->priority = -INFINITY;
1044         }
1045         return;
1046     }
1047 
1048     if ((constraint->primary_role != RSC_ROLE_UNKNOWN)
1049         && (constraint->primary_role != primary->next_role)) {
1050         return;
1051     }
1052 
1053     if (constraint->dependent_role == RSC_ROLE_UNPROMOTED) {
1054         score_multiplier = -1;
1055     }
1056 
1057     dependent->priority = pe__add_scores(score_multiplier * constraint->score,
1058                                          dependent->priority);
1059 }

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