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

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