root/lib/pacemaker/pcmk_sched_ordering.c

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

DEFINITIONS

This source file includes following definitions.
  1. invert_action
  2. get_ordering_type
  3. get_ordering_symmetry
  4. ordering_flags_for_kind
  5. get_ordering_resource
  6. get_minimum_first_instances
  7. clone_min_ordering
  8. inverse_ordering
  9. unpack_simple_rsc_order
  10. pcmk__new_ordering
  11. unpack_order_set
  12. order_rsc_sets
  13. unpack_order_tags
  14. pcmk__unpack_ordering
  15. ordering_is_invalid
  16. pcmk__disable_invalid_orderings
  17. pcmk__order_stops_before_shutdown
  18. find_actions_by_task
  19. order_resource_actions_after
  20. rsc_order_first
  21. block_colocation_dependents
  22. update_action_for_orderings
  23. pcmk__apply_orderings
  24. pcmk__order_after_each
  25. pcmk__promotable_restart_ordering

   1 /*
   2  * Copyright 2004-2025 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 <inttypes.h>               // PRIx32
  13 #include <stdbool.h>                // bool, true, false
  14 #include <glib.h>
  15 
  16 #include <crm/crm.h>
  17 #include <pacemaker-internal.h>
  18 #include "libpacemaker_private.h"
  19 
  20 enum pe_order_kind {
  21     pe_order_kind_optional,
  22     pe_order_kind_mandatory,
  23     pe_order_kind_serialize,
  24 };
  25 
  26 enum ordering_symmetry {
  27     ordering_asymmetric,        // the only relation in an asymmetric ordering
  28     ordering_symmetric,         // the normal relation in a symmetric ordering
  29     ordering_symmetric_inverse, // the inverse relation in a symmetric ordering
  30 };
  31 
  32 // @TODO de-functionize this for readability and possibly better log messages
  33 #define EXPAND_CONSTRAINT_IDREF(__set, __rsc, __name) do {                  \
  34         __rsc = pcmk__find_constraint_resource(scheduler->priv->resources,  \
  35                                                __name);                     \
  36         if (__rsc == NULL) {                                                \
  37             pcmk__config_err("%s: No resource found for %s", __set, __name);\
  38             return pcmk_rc_unpack_error;                                    \
  39         }                                                                   \
  40     } while (0)
  41 
  42 static const char *
  43 invert_action(const char *action)
     /* [previous][next][first][last][top][bottom][index][help] */
  44 {
  45     if (pcmk__str_eq(action, PCMK_ACTION_START, pcmk__str_none)) {
  46         return PCMK_ACTION_STOP;
  47 
  48     } else if (pcmk__str_eq(action, PCMK_ACTION_STOP, pcmk__str_none)) {
  49         return PCMK_ACTION_START;
  50 
  51     } else if (pcmk__str_eq(action, PCMK_ACTION_PROMOTE, pcmk__str_none)) {
  52         return PCMK_ACTION_DEMOTE;
  53 
  54     } else if (pcmk__str_eq(action, PCMK_ACTION_DEMOTE, pcmk__str_none)) {
  55         return PCMK_ACTION_PROMOTE;
  56 
  57     } else if (pcmk__str_eq(action, PCMK_ACTION_PROMOTED, pcmk__str_none)) {
  58         return PCMK_ACTION_DEMOTED;
  59 
  60     } else if (pcmk__str_eq(action, PCMK_ACTION_DEMOTED, pcmk__str_none)) {
  61         return PCMK_ACTION_PROMOTED;
  62 
  63     } else if (pcmk__str_eq(action, PCMK_ACTION_RUNNING, pcmk__str_none)) {
  64         return PCMK_ACTION_STOPPED;
  65 
  66     } else if (pcmk__str_eq(action, PCMK_ACTION_STOPPED, pcmk__str_none)) {
  67         return PCMK_ACTION_RUNNING;
  68     }
  69     pcmk__config_warn("Unknown action '%s' specified in order constraint",
  70                       action);
  71     return NULL;
  72 }
  73 
  74 static enum pe_order_kind
  75 get_ordering_type(const xmlNode *xml_obj)
     /* [previous][next][first][last][top][bottom][index][help] */
  76 {
  77     enum pe_order_kind kind_e = pe_order_kind_mandatory;
  78     const char *kind = crm_element_value(xml_obj, PCMK_XA_KIND);
  79 
  80     if (kind == NULL) {
  81         const char *score = crm_element_value(xml_obj, PCMK_XA_SCORE);
  82 
  83         kind_e = pe_order_kind_mandatory;
  84 
  85         if (score) {
  86             // @COMPAT deprecated informally since 1.0.7, formally since 2.0.1
  87             int score_i = 0;
  88 
  89             (void) pcmk_parse_score(score, &score_i, 0);
  90             if (score_i == 0) {
  91                 kind_e = pe_order_kind_optional;
  92             }
  93             pcmk__warn_once(pcmk__wo_order_score,
  94                             "Support for '" PCMK_XA_SCORE "' in "
  95                             PCMK_XE_RSC_ORDER " is deprecated and will be "
  96                             "removed in a future release "
  97                             "(use '" PCMK_XA_KIND "' instead)");
  98         }
  99 
 100     } else if (pcmk__str_eq(kind, PCMK_VALUE_MANDATORY, pcmk__str_none)) {
 101         kind_e = pe_order_kind_mandatory;
 102 
 103     } else if (pcmk__str_eq(kind, PCMK_VALUE_OPTIONAL, pcmk__str_none)) {
 104         kind_e = pe_order_kind_optional;
 105 
 106     } else if (pcmk__str_eq(kind, PCMK_VALUE_SERIALIZE, pcmk__str_none)) {
 107         kind_e = pe_order_kind_serialize;
 108 
 109     } else {
 110         pcmk__config_err("Resetting '" PCMK_XA_KIND "' for constraint %s to "
 111                          "'" PCMK_VALUE_MANDATORY "' because '%s' is not valid",
 112                          pcmk__s(pcmk__xe_id(xml_obj), "missing ID"), kind);
 113     }
 114     return kind_e;
 115 }
 116 
 117 /*!
 118  * \internal
 119  * \brief Get ordering symmetry from XML
 120  *
 121  * \param[in] xml_obj               Ordering XML
 122  * \param[in] parent_kind           Default ordering kind
 123  * \param[in] parent_symmetrical_s  Parent element's \c PCMK_XA_SYMMETRICAL
 124  *                                  setting, if any
 125  *
 126  * \retval ordering_symmetric   Ordering is symmetric
 127  * \retval ordering_asymmetric  Ordering is asymmetric
 128  */
 129 static enum ordering_symmetry
 130 get_ordering_symmetry(const xmlNode *xml_obj, enum pe_order_kind parent_kind,
     /* [previous][next][first][last][top][bottom][index][help] */
 131                       const char *parent_symmetrical_s)
 132 {
 133     int rc = pcmk_rc_ok;
 134     bool symmetric = false;
 135     enum pe_order_kind kind = parent_kind; // Default to parent's kind
 136 
 137     // Check ordering XML for explicit kind
 138     if ((crm_element_value(xml_obj, PCMK_XA_KIND) != NULL)
 139         || (crm_element_value(xml_obj, PCMK_XA_SCORE) != NULL)) {
 140         kind = get_ordering_type(xml_obj);
 141     }
 142 
 143     // Check ordering XML (and parent) for explicit PCMK_XA_SYMMETRICAL setting
 144     rc = pcmk__xe_get_bool_attr(xml_obj, PCMK_XA_SYMMETRICAL, &symmetric);
 145 
 146     if (rc != pcmk_rc_ok && parent_symmetrical_s != NULL) {
 147         symmetric = crm_is_true(parent_symmetrical_s);
 148         rc = pcmk_rc_ok;
 149     }
 150 
 151     if (rc == pcmk_rc_ok) {
 152         if (symmetric) {
 153             if (kind == pe_order_kind_serialize) {
 154                 pcmk__config_warn("Ignoring " PCMK_XA_SYMMETRICAL
 155                                   " for '%s' because not valid with "
 156                                   PCMK_XA_KIND " of '" PCMK_VALUE_SERIALIZE "'",
 157                                   pcmk__xe_id(xml_obj));
 158             } else {
 159                 return ordering_symmetric;
 160             }
 161         }
 162         return ordering_asymmetric;
 163     }
 164 
 165     // Use default symmetry
 166     if (kind == pe_order_kind_serialize) {
 167         return ordering_asymmetric;
 168     }
 169     return ordering_symmetric;
 170 }
 171 
 172 /*!
 173  * \internal
 174  * \brief Get ordering flags appropriate to ordering kind
 175  *
 176  * \param[in] kind      Ordering kind
 177  * \param[in] first     Action name for 'first' action
 178  * \param[in] symmetry  This ordering's symmetry role
 179  *
 180  * \return Minimal ordering flags appropriate to \p kind
 181  */
 182 static uint32_t
 183 ordering_flags_for_kind(enum pe_order_kind kind, const char *first,
     /* [previous][next][first][last][top][bottom][index][help] */
 184                         enum ordering_symmetry symmetry)
 185 {
 186     uint32_t flags = pcmk__ar_none; // so we trace-log all flags set
 187 
 188     switch (kind) {
 189         case pe_order_kind_optional:
 190             pcmk__set_relation_flags(flags, pcmk__ar_ordered);
 191             break;
 192 
 193         case pe_order_kind_serialize:
 194             /* This flag is not used anywhere directly but means the relation
 195              * will not match an equality comparison against pcmk__ar_none or
 196              * pcmk__ar_ordered.
 197              */
 198             pcmk__set_relation_flags(flags, pcmk__ar_serialize);
 199             break;
 200 
 201         case pe_order_kind_mandatory:
 202             pcmk__set_relation_flags(flags, pcmk__ar_ordered);
 203             switch (symmetry) {
 204                 case ordering_asymmetric:
 205                     pcmk__set_relation_flags(flags, pcmk__ar_asymmetric);
 206                     break;
 207 
 208                 case ordering_symmetric:
 209                     pcmk__set_relation_flags(flags,
 210                                              pcmk__ar_first_implies_then);
 211                     if (pcmk__is_up_action(first)) {
 212                         pcmk__set_relation_flags(flags,
 213                                                  pcmk__ar_unrunnable_first_blocks);
 214                     }
 215                     break;
 216 
 217                 case ordering_symmetric_inverse:
 218                     pcmk__set_relation_flags(flags,
 219                                              pcmk__ar_then_implies_first);
 220                     break;
 221             }
 222             break;
 223     }
 224     return flags;
 225 }
 226 
 227 /*!
 228  * \internal
 229  * \brief Find resource corresponding to ID specified in ordering
 230  *
 231  * \param[in] xml            Ordering XML
 232  * \param[in] resource_attr  XML attribute name for resource ID
 233  * \param[in] scheduler      Scheduler data
 234  *
 235  * \return Resource corresponding to \p id, or NULL if none
 236  */
 237 static pcmk_resource_t *
 238 get_ordering_resource(const xmlNode *xml, const char *resource_attr,
     /* [previous][next][first][last][top][bottom][index][help] */
 239                       const pcmk_scheduler_t *scheduler)
 240 {
 241     pcmk_resource_t *rsc = NULL;
 242     const char *rsc_id = crm_element_value(xml, resource_attr);
 243 
 244     if (rsc_id == NULL) {
 245         pcmk__config_err("Ignoring constraint '%s' without %s",
 246                          pcmk__xe_id(xml), resource_attr);
 247         return NULL;
 248     }
 249 
 250     rsc = pcmk__find_constraint_resource(scheduler->priv->resources, rsc_id);
 251     if (rsc == NULL) {
 252         pcmk__config_err("Ignoring constraint '%s' because resource '%s' "
 253                          "does not exist", pcmk__xe_id(xml), rsc_id);
 254         return NULL;
 255     }
 256 
 257     return rsc;
 258 }
 259 
 260 /*!
 261  * \internal
 262  * \brief Determine minimum number of 'first' instances required in ordering
 263  *
 264  * \param[in] rsc  'First' resource in ordering
 265  * \param[in] xml  Ordering XML
 266  *
 267  * \return Minimum 'first' instances required (or 0 if not applicable)
 268  */
 269 static int
 270 get_minimum_first_instances(const pcmk_resource_t *rsc, const xmlNode *xml)
     /* [previous][next][first][last][top][bottom][index][help] */
 271 {
 272     const char *clone_min = NULL;
 273     bool require_all = false;
 274 
 275     if (!pcmk__is_clone(rsc)) {
 276         return 0;
 277     }
 278 
 279     clone_min = g_hash_table_lookup(rsc->priv->meta, PCMK_META_CLONE_MIN);
 280     if (clone_min != NULL) {
 281         int clone_min_int = 0;
 282 
 283         pcmk__scan_min_int(clone_min, &clone_min_int, 0);
 284         return clone_min_int;
 285     }
 286 
 287     /* @COMPAT 1.1.13:
 288      * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE is deprecated equivalent of
 289      * PCMK_META_CLONE_MIN=1
 290      */
 291     if (pcmk__xe_get_bool_attr(xml, PCMK_XA_REQUIRE_ALL,
 292                                &require_all) != ENODATA) {
 293         pcmk__warn_once(pcmk__wo_require_all,
 294                         "Support for " PCMK_XA_REQUIRE_ALL " in ordering "
 295                         "constraints is deprecated and will be removed in a "
 296                         "future release (use " PCMK_META_CLONE_MIN " clone "
 297                         "meta-attribute instead)");
 298         if (!require_all) {
 299             return 1;
 300         }
 301     }
 302 
 303     return 0;
 304 }
 305 
 306 /*!
 307  * \internal
 308  * \brief Create orderings for a constraint with \c PCMK_META_CLONE_MIN > 0
 309  *
 310  * \param[in]     id            Ordering ID
 311  * \param[in,out] rsc_first     'First' resource in ordering (a clone)
 312  * \param[in]     action_first  'First' action in ordering
 313  * \param[in]     rsc_then      'Then' resource in ordering
 314  * \param[in]     action_then   'Then' action in ordering
 315  * \param[in]     flags         Ordering flags
 316  * \param[in]     clone_min     Minimum required instances of 'first'
 317  */
 318 static void
 319 clone_min_ordering(const char *id,
     /* [previous][next][first][last][top][bottom][index][help] */
 320                    pcmk_resource_t *rsc_first, const char *action_first,
 321                    pcmk_resource_t *rsc_then, const char *action_then,
 322                    uint32_t flags, int clone_min)
 323 {
 324     // Create a pseudo-action for when the minimum instances are active
 325     char *task = crm_strdup_printf(PCMK_ACTION_CLONE_ONE_OR_MORE ":%s", id);
 326     pcmk_action_t *clone_min_met = get_pseudo_op(task,
 327                                                  rsc_first->priv->scheduler);
 328 
 329     free(task);
 330 
 331     /* Require the pseudo-action to have the required number of actions to be
 332      * considered runnable before allowing the pseudo-action to be runnable.
 333      */
 334     clone_min_met->required_runnable_before = clone_min;
 335 
 336     // Order the actions for each clone instance before the pseudo-action
 337     for (GList *iter = rsc_first->priv->children;
 338          iter != NULL; iter = iter->next) {
 339 
 340         pcmk_resource_t *child = iter->data;
 341 
 342         pcmk__new_ordering(child, pcmk__op_key(child->id, action_first, 0),
 343                            NULL, NULL, NULL, clone_min_met,
 344                            pcmk__ar_min_runnable
 345                            |pcmk__ar_first_implies_then_graphed,
 346                            rsc_first->priv->scheduler);
 347     }
 348 
 349     // Order "then" action after the pseudo-action (if runnable)
 350     pcmk__new_ordering(NULL, NULL, clone_min_met, rsc_then,
 351                        pcmk__op_key(rsc_then->id, action_then, 0),
 352                        NULL, flags|pcmk__ar_unrunnable_first_blocks,
 353                        rsc_first->priv->scheduler);
 354 }
 355 
 356 /*!
 357  * \internal
 358  * \brief Create new ordering for inverse of symmetric constraint
 359  *
 360  * \param[in]     id            Ordering ID (for logging only)
 361  * \param[in]     kind          Ordering kind
 362  * \param[in]     rsc_first     'First' resource in ordering (a clone)
 363  * \param[in]     action_first  'First' action in ordering
 364  * \param[in,out] rsc_then      'Then' resource in ordering
 365  * \param[in]     action_then   'Then' action in ordering
 366  */
 367 static void
 368 inverse_ordering(const char *id, enum pe_order_kind kind,
     /* [previous][next][first][last][top][bottom][index][help] */
 369                  pcmk_resource_t *rsc_first, const char *action_first,
 370                  pcmk_resource_t *rsc_then, const char *action_then)
 371 {
 372     uint32_t flags;
 373     const char *inverted_first = invert_action(action_first);
 374     const char *inverted_then = invert_action(action_then);
 375 
 376     if ((inverted_then == NULL) || (inverted_first == NULL)) {
 377         pcmk__config_warn("Cannot invert constraint '%s' "
 378                           "(please specify inverse manually)", id);
 379         return;
 380     }
 381 
 382     // Order inverted actions
 383     flags = ordering_flags_for_kind(kind, inverted_first,
 384                                     ordering_symmetric_inverse);
 385     pcmk__order_resource_actions(rsc_then, inverted_then,
 386                                  rsc_first, inverted_first, flags);
 387 }
 388 
 389 static void
 390 unpack_simple_rsc_order(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 391 {
 392     pcmk_resource_t *rsc_then = NULL;
 393     pcmk_resource_t *rsc_first = NULL;
 394     int min_required_before = 0;
 395     enum pe_order_kind kind = pe_order_kind_mandatory;
 396     uint32_t flags = pcmk__ar_none;
 397     enum ordering_symmetry symmetry;
 398 
 399     const char *action_then = NULL;
 400     const char *action_first = NULL;
 401     const char *id = NULL;
 402 
 403     CRM_CHECK(xml_obj != NULL, return);
 404 
 405     id = crm_element_value(xml_obj, PCMK_XA_ID);
 406     if (id == NULL) {
 407         pcmk__config_err("Ignoring <%s> constraint without " PCMK_XA_ID,
 408                          xml_obj->name);
 409         return;
 410     }
 411 
 412     rsc_first = get_ordering_resource(xml_obj, PCMK_XA_FIRST, scheduler);
 413     if (rsc_first == NULL) {
 414         return;
 415     }
 416 
 417     rsc_then = get_ordering_resource(xml_obj, PCMK_XA_THEN, scheduler);
 418     if (rsc_then == NULL) {
 419         return;
 420     }
 421 
 422     action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
 423     if (action_first == NULL) {
 424         action_first = PCMK_ACTION_START;
 425     }
 426 
 427     action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
 428     if (action_then == NULL) {
 429         action_then = action_first;
 430     }
 431 
 432     kind = get_ordering_type(xml_obj);
 433 
 434     symmetry = get_ordering_symmetry(xml_obj, kind, NULL);
 435     flags = ordering_flags_for_kind(kind, action_first, symmetry);
 436 
 437     /* If there is a minimum number of instances that must be runnable before
 438      * the 'then' action is runnable, we use a pseudo-action for convenience:
 439      * minimum number of clone instances have runnable actions ->
 440      * pseudo-action is runnable -> dependency is runnable.
 441      */
 442     min_required_before = get_minimum_first_instances(rsc_first, xml_obj);
 443     if (min_required_before > 0) {
 444         clone_min_ordering(id, rsc_first, action_first, rsc_then, action_then,
 445                            flags, min_required_before);
 446     } else {
 447         pcmk__order_resource_actions(rsc_first, action_first, rsc_then,
 448                                      action_then, flags);
 449     }
 450 
 451     if (symmetry == ordering_symmetric) {
 452         inverse_ordering(id, kind, rsc_first, action_first,
 453                          rsc_then, action_then);
 454     }
 455 }
 456 
 457 /*!
 458  * \internal
 459  * \brief Create a new ordering between two actions
 460  *
 461  * \param[in,out] first_rsc          Resource for 'first' action (if NULL and
 462  *                                   \p first_action is a resource action, that
 463  *                                   resource will be used)
 464  * \param[in,out] first_action_task  Action key for 'first' action (if NULL and
 465  *                                   \p first_action is not NULL, its UUID will
 466  *                                   be used)
 467  * \param[in,out] first_action       'first' action (if NULL, \p first_rsc and
 468  *                                   \p first_action_task must be set)
 469  *
 470  * \param[in]     then_rsc           Resource for 'then' action (if NULL and
 471  *                                   \p then_action is a resource action, that
 472  *                                   resource will be used)
 473  * \param[in,out] then_action_task   Action key for 'then' action (if NULL and
 474  *                                   \p then_action is not NULL, its UUID will
 475  *                                   be used)
 476  * \param[in]     then_action        'then' action (if NULL, \p then_rsc and
 477  *                                   \p then_action_task must be set)
 478  *
 479  * \param[in]     flags              Group of enum pcmk__action_relation_flags
 480  * \param[in,out] sched              Scheduler data to add ordering to
 481  *
 482  * \note This function takes ownership of first_action_task and
 483  *       then_action_task, which do not need to be freed by the caller.
 484  */
 485 void
 486 pcmk__new_ordering(pcmk_resource_t *first_rsc, char *first_action_task,
     /* [previous][next][first][last][top][bottom][index][help] */
 487                    pcmk_action_t *first_action, pcmk_resource_t *then_rsc,
 488                    char *then_action_task, pcmk_action_t *then_action,
 489                    uint32_t flags, pcmk_scheduler_t *sched)
 490 {
 491     pcmk__action_relation_t *order = NULL;
 492 
 493     // One of action or resource must be specified for each side
 494     CRM_CHECK(((first_action != NULL) || (first_rsc != NULL))
 495               && ((then_action != NULL) || (then_rsc != NULL)),
 496               free(first_action_task); free(then_action_task); return);
 497 
 498     if ((first_rsc == NULL) && (first_action != NULL)) {
 499         first_rsc = first_action->rsc;
 500     }
 501     if ((then_rsc == NULL) && (then_action != NULL)) {
 502         then_rsc = then_action->rsc;
 503     }
 504 
 505     order = pcmk__assert_alloc(1, sizeof(pcmk__action_relation_t));
 506 
 507     order->id = sched->priv->next_ordering_id++;
 508     order->flags = flags;
 509     order->rsc1 = first_rsc;
 510     order->rsc2 = then_rsc;
 511     order->action1 = first_action;
 512     order->action2 = then_action;
 513     order->task1 = first_action_task;
 514     order->task2 = then_action_task;
 515 
 516     if ((order->task1 == NULL) && (first_action != NULL)) {
 517         order->task1 = strdup(first_action->uuid);
 518     }
 519 
 520     if ((order->task2 == NULL) && (then_action != NULL)) {
 521         order->task2 = strdup(then_action->uuid);
 522     }
 523 
 524     if ((order->rsc1 == NULL) && (first_action != NULL)) {
 525         order->rsc1 = first_action->rsc;
 526     }
 527 
 528     if ((order->rsc2 == NULL) && (then_action != NULL)) {
 529         order->rsc2 = then_action->rsc;
 530     }
 531 
 532     pcmk__rsc_trace(first_rsc, "Created ordering %d for %s then %s",
 533                     (sched->priv->next_ordering_id - 1),
 534                     pcmk__s(order->task1, "an underspecified action"),
 535                     pcmk__s(order->task2, "an underspecified action"));
 536 
 537     sched->priv->ordering_constraints =
 538         g_list_prepend(sched->priv->ordering_constraints, order);
 539     pcmk__order_migration_equivalents(order);
 540 }
 541 
 542 /*!
 543  * \brief Unpack a set in an ordering constraint
 544  *
 545  * \param[in]     set                   Set XML to unpack
 546  * \param[in]     parent_kind           \c PCMK_XE_RSC_ORDER XML \c PCMK_XA_KIND
 547  *                                      attribute
 548  * \param[in]     parent_symmetrical_s  \c PCMK_XE_RSC_ORDER XML
 549  *                                      \c PCMK_XA_SYMMETRICAL attribute
 550  * \param[in,out] scheduler             Scheduler data
 551  *
 552  * \return Standard Pacemaker return code
 553  */
 554 static int
 555 unpack_order_set(const xmlNode *set, enum pe_order_kind parent_kind,
     /* [previous][next][first][last][top][bottom][index][help] */
 556                  const char *parent_symmetrical_s, pcmk_scheduler_t *scheduler)
 557 {
 558     GList *set_iter = NULL;
 559     GList *resources = NULL;
 560 
 561     pcmk_resource_t *last = NULL;
 562     pcmk_resource_t *resource = NULL;
 563 
 564     int local_kind = parent_kind;
 565     bool sequential = false;
 566     uint32_t flags = pcmk__ar_ordered;
 567     enum ordering_symmetry symmetry;
 568 
 569     char *key = NULL;
 570     const char *id = pcmk__xe_id(set);
 571     const char *action = crm_element_value(set, PCMK_XA_ACTION);
 572     const char *sequential_s = crm_element_value(set, PCMK_XA_SEQUENTIAL);
 573     const char *kind_s = crm_element_value(set, PCMK_XA_KIND);
 574 
 575     if (action == NULL) {
 576         action = PCMK_ACTION_START;
 577     }
 578 
 579     if (kind_s) {
 580         local_kind = get_ordering_type(set);
 581     }
 582     if (sequential_s == NULL) {
 583         sequential_s = "1";
 584     }
 585 
 586     sequential = crm_is_true(sequential_s);
 587 
 588     symmetry = get_ordering_symmetry(set, parent_kind, parent_symmetrical_s);
 589     flags = ordering_flags_for_kind(local_kind, action, symmetry);
 590 
 591     for (const xmlNode *xml_rsc = pcmk__xe_first_child(set,
 592                                                        PCMK_XE_RESOURCE_REF,
 593                                                        NULL, NULL);
 594          xml_rsc != NULL;
 595          xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 596 
 597         EXPAND_CONSTRAINT_IDREF(id, resource, pcmk__xe_id(xml_rsc));
 598         resources = g_list_append(resources, resource);
 599     }
 600 
 601     if (pcmk__list_of_1(resources)) {
 602         crm_trace("Single set: %s", id);
 603         goto done;
 604     }
 605 
 606     set_iter = resources;
 607     while (set_iter != NULL) {
 608         resource = (pcmk_resource_t *) set_iter->data;
 609         set_iter = set_iter->next;
 610 
 611         key = pcmk__op_key(resource->id, action, 0);
 612 
 613         if (local_kind == pe_order_kind_serialize) {
 614             /* Serialize before everything that comes after */
 615 
 616             for (GList *iter = set_iter; iter != NULL; iter = iter->next) {
 617                 pcmk_resource_t *then_rsc = iter->data;
 618                 char *then_key = pcmk__op_key(then_rsc->id, action, 0);
 619 
 620                 pcmk__new_ordering(resource, strdup(key), NULL, then_rsc,
 621                                    then_key, NULL, flags, scheduler);
 622             }
 623 
 624         } else if (sequential) {
 625             if (last != NULL) {
 626                 pcmk__order_resource_actions(last, action, resource, action,
 627                                              flags);
 628             }
 629             last = resource;
 630         }
 631         free(key);
 632     }
 633 
 634     if (symmetry == ordering_asymmetric) {
 635         goto done;
 636     }
 637 
 638     last = NULL;
 639     action = invert_action(action);
 640 
 641     flags = ordering_flags_for_kind(local_kind, action,
 642                                     ordering_symmetric_inverse);
 643 
 644     set_iter = resources;
 645     while (set_iter != NULL) {
 646         resource = (pcmk_resource_t *) set_iter->data;
 647         set_iter = set_iter->next;
 648 
 649         if (sequential) {
 650             if (last != NULL) {
 651                 pcmk__order_resource_actions(resource, action, last, action,
 652                                              flags);
 653             }
 654             last = resource;
 655         }
 656     }
 657 
 658   done:
 659     g_list_free(resources);
 660     return pcmk_rc_ok;
 661 }
 662 
 663 /*!
 664  * \brief Order two resource sets relative to each other
 665  *
 666  * \param[in]     id         Ordering ID (for logging)
 667  * \param[in]     set1       First listed set
 668  * \param[in]     set2       Second listed set
 669  * \param[in]     kind       Ordering kind
 670  * \param[in,out] scheduler  Scheduler data
 671  * \param[in]     symmetry   Which ordering symmetry applies to this relation
 672  *
 673  * \return Standard Pacemaker return code
 674  */
 675 static int
 676 order_rsc_sets(const char *id, const xmlNode *set1, const xmlNode *set2,
     /* [previous][next][first][last][top][bottom][index][help] */
 677                enum pe_order_kind kind, pcmk_scheduler_t *scheduler,
 678                enum ordering_symmetry symmetry)
 679 {
 680 
 681     const xmlNode *xml_rsc = NULL;
 682     const xmlNode *xml_rsc_2 = NULL;
 683 
 684     pcmk_resource_t *rsc_1 = NULL;
 685     pcmk_resource_t *rsc_2 = NULL;
 686 
 687     const char *action_1 = crm_element_value(set1, PCMK_XA_ACTION);
 688     const char *action_2 = crm_element_value(set2, PCMK_XA_ACTION);
 689 
 690     uint32_t flags = pcmk__ar_none;
 691 
 692     bool require_all = true;
 693 
 694     (void) pcmk__xe_get_bool_attr(set1, PCMK_XA_REQUIRE_ALL, &require_all);
 695 
 696     if (action_1 == NULL) {
 697         action_1 = PCMK_ACTION_START;
 698     }
 699 
 700     if (action_2 == NULL) {
 701         action_2 = PCMK_ACTION_START;
 702     }
 703 
 704     if (symmetry == ordering_symmetric_inverse) {
 705         action_1 = invert_action(action_1);
 706         action_2 = invert_action(action_2);
 707     }
 708 
 709     if (pcmk__str_eq(PCMK_ACTION_STOP, action_1, pcmk__str_none)
 710         || pcmk__str_eq(PCMK_ACTION_DEMOTE, action_1, pcmk__str_none)) {
 711         /* Assuming: A -> ( B || C) -> D
 712          * The one-or-more logic only applies during the start/promote phase.
 713          * During shutdown neither B nor can shutdown until D is down, so simply
 714          * turn require_all back on.
 715          */
 716         require_all = true;
 717     }
 718 
 719     flags = ordering_flags_for_kind(kind, action_1, symmetry);
 720 
 721     /* If we have an unordered set1, whether it is sequential or not is
 722      * irrelevant in regards to set2.
 723      */
 724     if (!require_all) {
 725         char *task = crm_strdup_printf(PCMK_ACTION_ONE_OR_MORE ":%s",
 726                                        pcmk__xe_id(set1));
 727         pcmk_action_t *unordered_action = get_pseudo_op(task, scheduler);
 728 
 729         free(task);
 730         unordered_action->required_runnable_before = 1;
 731 
 732         for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
 733                                             NULL);
 734              xml_rsc != NULL;
 735              xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 736 
 737             EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
 738 
 739             /* Add an ordering constraint between every element in set1 and the
 740              * pseudo action. If any action in set1 is runnable the pseudo
 741              * action will be runnable.
 742              */
 743             pcmk__new_ordering(rsc_1, pcmk__op_key(rsc_1->id, action_1, 0),
 744                                NULL, NULL, NULL, unordered_action,
 745                                pcmk__ar_min_runnable
 746                                |pcmk__ar_first_implies_then_graphed,
 747                                scheduler);
 748         }
 749         for (xml_rsc_2 = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
 750                                               NULL);
 751              xml_rsc_2 != NULL;
 752              xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
 753 
 754             EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
 755 
 756             /* Add an ordering constraint between the pseudo-action and every
 757              * element in set2. If the pseudo-action is runnable, every action
 758              * in set2 will be runnable.
 759              */
 760             pcmk__new_ordering(NULL, NULL, unordered_action,
 761                                rsc_2, pcmk__op_key(rsc_2->id, action_2, 0),
 762                                NULL, flags|pcmk__ar_unrunnable_first_blocks,
 763                                scheduler);
 764         }
 765 
 766         return pcmk_rc_ok;
 767     }
 768 
 769     if (pcmk__xe_attr_is_true(set1, PCMK_XA_SEQUENTIAL)) {
 770         if (symmetry == ordering_symmetric_inverse) {
 771             // Get the first one
 772             xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
 773                                            NULL);
 774             if (xml_rsc != NULL) {
 775                 EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
 776             }
 777 
 778         } else {
 779             // Get the last one
 780             const char *rid = NULL;
 781 
 782             for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF,
 783                                                 NULL, NULL);
 784                  xml_rsc != NULL;
 785                  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 786 
 787                 rid = pcmk__xe_id(xml_rsc);
 788             }
 789             EXPAND_CONSTRAINT_IDREF(id, rsc_1, rid);
 790         }
 791     }
 792 
 793     if (pcmk__xe_attr_is_true(set2, PCMK_XA_SEQUENTIAL)) {
 794         if (symmetry == ordering_symmetric_inverse) {
 795             // Get the last one
 796             const char *rid = NULL;
 797 
 798             for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF,
 799                                                 NULL, NULL);
 800                  xml_rsc != NULL;
 801                  xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 802 
 803                 rid = pcmk__xe_id(xml_rsc);
 804             }
 805             EXPAND_CONSTRAINT_IDREF(id, rsc_2, rid);
 806 
 807         } else {
 808             // Get the first one
 809             xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
 810                                            NULL);
 811             if (xml_rsc != NULL) {
 812                 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
 813             }
 814         }
 815     }
 816 
 817     if ((rsc_1 != NULL) && (rsc_2 != NULL)) {
 818         pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2, flags);
 819 
 820     } else if (rsc_1 != NULL) {
 821         for (xml_rsc = pcmk__xe_first_child(set2, PCMK_XE_RESOURCE_REF, NULL,
 822                                             NULL);
 823              xml_rsc != NULL;
 824              xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 825 
 826             EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc));
 827             pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
 828                                          flags);
 829         }
 830 
 831     } else if (rsc_2 != NULL) {
 832         for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
 833                                             NULL);
 834              xml_rsc != NULL;
 835              xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 836 
 837             EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
 838             pcmk__order_resource_actions(rsc_1, action_1, rsc_2, action_2,
 839                                          flags);
 840         }
 841 
 842     } else {
 843         for (xml_rsc = pcmk__xe_first_child(set1, PCMK_XE_RESOURCE_REF, NULL,
 844                                             NULL);
 845              xml_rsc != NULL;
 846              xml_rsc = pcmk__xe_next(xml_rsc, PCMK_XE_RESOURCE_REF)) {
 847 
 848             EXPAND_CONSTRAINT_IDREF(id, rsc_1, pcmk__xe_id(xml_rsc));
 849 
 850             for (xmlNode *xml_rsc_2 = pcmk__xe_first_child(set2,
 851                                                            PCMK_XE_RESOURCE_REF,
 852                                                            NULL, NULL);
 853                  xml_rsc_2 != NULL;
 854                  xml_rsc_2 = pcmk__xe_next(xml_rsc_2, PCMK_XE_RESOURCE_REF)) {
 855 
 856                 EXPAND_CONSTRAINT_IDREF(id, rsc_2, pcmk__xe_id(xml_rsc_2));
 857                 pcmk__order_resource_actions(rsc_1, action_1, rsc_2,
 858                                              action_2, flags);
 859             }
 860         }
 861     }
 862 
 863     return pcmk_rc_ok;
 864 }
 865 
 866 /*!
 867  * \internal
 868  * \brief If an ordering constraint uses resource tags, expand them
 869  *
 870  * \param[in,out] xml_obj       Ordering constraint XML
 871  * \param[out]    expanded_xml  Equivalent XML with tags expanded
 872  * \param[in]     scheduler     Scheduler data
 873  *
 874  * \return Standard Pacemaker return code (specifically, pcmk_rc_ok on success,
 875  *         and pcmk_rc_unpack_error on invalid configuration)
 876  */
 877 static int
 878 unpack_order_tags(xmlNode *xml_obj, xmlNode **expanded_xml,
     /* [previous][next][first][last][top][bottom][index][help] */
 879                   const pcmk_scheduler_t *scheduler)
 880 {
 881     const char *id_first = NULL;
 882     const char *id_then = NULL;
 883     const char *action_first = NULL;
 884     const char *action_then = NULL;
 885 
 886     pcmk_resource_t *rsc_first = NULL;
 887     pcmk_resource_t *rsc_then = NULL;
 888     pcmk__idref_t *tag_first = NULL;
 889     pcmk__idref_t *tag_then = NULL;
 890 
 891     xmlNode *rsc_set_first = NULL;
 892     xmlNode *rsc_set_then = NULL;
 893     bool any_sets = false;
 894 
 895     // Check whether there are any resource sets with template or tag references
 896     *expanded_xml = pcmk__expand_tags_in_sets(xml_obj, scheduler);
 897     if (*expanded_xml != NULL) {
 898         crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
 899         return pcmk_rc_ok;
 900     }
 901 
 902     id_first = crm_element_value(xml_obj, PCMK_XA_FIRST);
 903     id_then = crm_element_value(xml_obj, PCMK_XA_THEN);
 904     if ((id_first == NULL) || (id_then == NULL)) {
 905         return pcmk_rc_ok;
 906     }
 907 
 908     if (!pcmk__valid_resource_or_tag(scheduler, id_first, &rsc_first,
 909                                      &tag_first)) {
 910         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 911                          "valid resource or tag",
 912                          pcmk__xe_id(xml_obj), id_first);
 913         return pcmk_rc_unpack_error;
 914     }
 915 
 916     if (!pcmk__valid_resource_or_tag(scheduler, id_then, &rsc_then,
 917                                      &tag_then)) {
 918         pcmk__config_err("Ignoring constraint '%s' because '%s' is not a "
 919                          "valid resource or tag",
 920                          pcmk__xe_id(xml_obj), id_then);
 921         return pcmk_rc_unpack_error;
 922     }
 923 
 924     if ((rsc_first != NULL) && (rsc_then != NULL)) {
 925         // Neither side references a template or tag
 926         return pcmk_rc_ok;
 927     }
 928 
 929     action_first = crm_element_value(xml_obj, PCMK_XA_FIRST_ACTION);
 930     action_then = crm_element_value(xml_obj, PCMK_XA_THEN_ACTION);
 931 
 932     *expanded_xml = pcmk__xml_copy(NULL, xml_obj);
 933 
 934     /* Convert template/tag reference in PCMK_XA_FIRST into constraint
 935      * PCMK_XE_RESOURCE_SET
 936      */
 937     if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_first, PCMK_XA_FIRST, true,
 938                           scheduler)) {
 939         pcmk__xml_free(*expanded_xml);
 940         *expanded_xml = NULL;
 941         return pcmk_rc_unpack_error;
 942     }
 943 
 944     if (rsc_set_first != NULL) {
 945         if (action_first != NULL) {
 946             /* Move PCMK_XA_FIRST_ACTION into converted PCMK_XE_RESOURCE_SET as
 947              * PCMK_XA_ACTION
 948              */
 949             crm_xml_add(rsc_set_first, PCMK_XA_ACTION, action_first);
 950             pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_FIRST_ACTION);
 951         }
 952         any_sets = true;
 953     }
 954 
 955     /* Convert template/tag reference in PCMK_XA_THEN into constraint
 956      * PCMK_XE_RESOURCE_SET
 957      */
 958     if (!pcmk__tag_to_set(*expanded_xml, &rsc_set_then, PCMK_XA_THEN, true,
 959                           scheduler)) {
 960         pcmk__xml_free(*expanded_xml);
 961         *expanded_xml = NULL;
 962         return pcmk_rc_unpack_error;
 963     }
 964 
 965     if (rsc_set_then != NULL) {
 966         if (action_then != NULL) {
 967             /* Move PCMK_XA_THEN_ACTION into converted PCMK_XE_RESOURCE_SET as
 968              * PCMK_XA_ACTION
 969              */
 970             crm_xml_add(rsc_set_then, PCMK_XA_ACTION, action_then);
 971             pcmk__xe_remove_attr(*expanded_xml, PCMK_XA_THEN_ACTION);
 972         }
 973         any_sets = true;
 974     }
 975 
 976     if (any_sets) {
 977         crm_log_xml_trace(*expanded_xml, "Expanded " PCMK_XE_RSC_ORDER);
 978     } else {
 979         pcmk__xml_free(*expanded_xml);
 980         *expanded_xml = NULL;
 981     }
 982 
 983     return pcmk_rc_ok;
 984 }
 985 
 986 /*!
 987  * \internal
 988  * \brief Unpack ordering constraint XML
 989  *
 990  * \param[in,out] xml_obj    Ordering constraint XML to unpack
 991  * \param[in,out] scheduler  Scheduler data
 992  */
 993 void
 994 pcmk__unpack_ordering(xmlNode *xml_obj, pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
 995 {
 996     xmlNode *set = NULL;
 997     xmlNode *last = NULL;
 998 
 999     xmlNode *orig_xml = NULL;
1000     xmlNode *expanded_xml = NULL;
1001 
1002     const char *id = crm_element_value(xml_obj, PCMK_XA_ID);
1003     const char *invert = crm_element_value(xml_obj, PCMK_XA_SYMMETRICAL);
1004     enum pe_order_kind kind = get_ordering_type(xml_obj);
1005 
1006     enum ordering_symmetry symmetry = get_ordering_symmetry(xml_obj, kind,
1007                                                             NULL);
1008 
1009     // Expand any resource tags in the constraint XML
1010     if (unpack_order_tags(xml_obj, &expanded_xml, scheduler) != pcmk_rc_ok) {
1011         return;
1012     }
1013     if (expanded_xml != NULL) {
1014         orig_xml = xml_obj;
1015         xml_obj = expanded_xml;
1016     }
1017 
1018     // If the constraint has resource sets, unpack them
1019     for (set = pcmk__xe_first_child(xml_obj, PCMK_XE_RESOURCE_SET, NULL, NULL);
1020          set != NULL; set = pcmk__xe_next(set, PCMK_XE_RESOURCE_SET)) {
1021 
1022         set = pcmk__xe_resolve_idref(set, scheduler->input);
1023         if ((set == NULL) // Configuration error, message already logged
1024             || (unpack_order_set(set, kind, invert, scheduler) != pcmk_rc_ok)) {
1025 
1026             if (expanded_xml != NULL) {
1027                 pcmk__xml_free(expanded_xml);
1028             }
1029             return;
1030         }
1031 
1032         if (last != NULL) {
1033 
1034             if (order_rsc_sets(id, last, set, kind, scheduler,
1035                                symmetry) != pcmk_rc_ok) {
1036                 if (expanded_xml != NULL) {
1037                     pcmk__xml_free(expanded_xml);
1038                 }
1039                 return;
1040             }
1041 
1042             if ((symmetry == ordering_symmetric)
1043                 && (order_rsc_sets(id, set, last, kind, scheduler,
1044                                    ordering_symmetric_inverse) != pcmk_rc_ok)) {
1045                 if (expanded_xml != NULL) {
1046                     pcmk__xml_free(expanded_xml);
1047                 }
1048                 return;
1049             }
1050 
1051         }
1052         last = set;
1053     }
1054 
1055     if (expanded_xml) {
1056         pcmk__xml_free(expanded_xml);
1057         xml_obj = orig_xml;
1058     }
1059 
1060     // If the constraint has no resource sets, unpack it as a simple ordering
1061     if (last == NULL) {
1062         return unpack_simple_rsc_order(xml_obj, scheduler);
1063     }
1064 }
1065 
1066 static bool
1067 ordering_is_invalid(pcmk_action_t *action, pcmk__related_action_t *input)
     /* [previous][next][first][last][top][bottom][index][help] */
1068 {
1069     /* Prevent user-defined ordering constraints between resources
1070      * running in a guest node and the resource that defines that node.
1071      */
1072     if (!pcmk_is_set(input->flags, pcmk__ar_guest_allowed)
1073         && (input->action->rsc != NULL)
1074         && pcmk__rsc_corresponds_to_guest(action->rsc, input->action->node)) {
1075 
1076         pcmk__config_warn("Invalid ordering constraint between %s and %s",
1077                           input->action->rsc->id, action->rsc->id);
1078         return true;
1079     }
1080 
1081     /* If there's an order like
1082      * "rscB_stop node2"-> "load_stopped_node2" -> "rscA_migrate_to node1"
1083      *
1084      * then rscA is being migrated from node1 to node2, while rscB is being
1085      * migrated from node2 to node1. If there would be a graph loop,
1086      * break the order "load_stopped_node2" -> "rscA_migrate_to node1".
1087      */
1088     if ((input->flags == pcmk__ar_if_on_same_node_or_target)
1089         && (action->rsc != NULL)
1090         && pcmk__str_eq(action->task, PCMK_ACTION_MIGRATE_TO, pcmk__str_none)
1091         && pcmk__graph_has_loop(action, action, input)) {
1092         return true;
1093     }
1094 
1095     return false;
1096 }
1097 
1098 void
1099 pcmk__disable_invalid_orderings(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
1100 {
1101     for (GList *iter = scheduler->priv->actions;
1102          iter != NULL; iter = iter->next) {
1103 
1104         pcmk_action_t *action = (pcmk_action_t *) iter->data;
1105         pcmk__related_action_t *input = NULL;
1106 
1107         for (GList *input_iter = action->actions_before;
1108              input_iter != NULL; input_iter = input_iter->next) {
1109 
1110             input = input_iter->data;
1111             if (ordering_is_invalid(action, input)) {
1112                 input->flags = pcmk__ar_none;
1113             }
1114         }
1115     }
1116 }
1117 
1118 /*!
1119  * \internal
1120  * \brief Order stops on a node before the node's shutdown
1121  *
1122  * \param[in,out] node         Node being shut down
1123  * \param[in]     shutdown_op  Shutdown action for node
1124  */
1125 void
1126 pcmk__order_stops_before_shutdown(pcmk_node_t *node, pcmk_action_t *shutdown_op)
     /* [previous][next][first][last][top][bottom][index][help] */
1127 {
1128     for (GList *iter = node->priv->scheduler->priv->actions;
1129          iter != NULL; iter = iter->next) {
1130 
1131         pcmk_action_t *action = (pcmk_action_t *) iter->data;
1132 
1133         // Only stops on the node shutting down are relevant
1134         if (!pcmk__same_node(action->node, node)
1135             || !pcmk__str_eq(action->task, PCMK_ACTION_STOP, pcmk__str_none)) {
1136             continue;
1137         }
1138 
1139         // Resources and nodes in maintenance mode won't be touched
1140 
1141         if (pcmk_is_set(action->rsc->flags, pcmk__rsc_maintenance)) {
1142             pcmk__rsc_trace(action->rsc,
1143                             "Not ordering %s before shutdown of %s because "
1144                             "resource in maintenance mode",
1145                             action->uuid, pcmk__node_name(node));
1146             continue;
1147 
1148         } else if (node->details->maintenance) {
1149             pcmk__rsc_trace(action->rsc,
1150                             "Not ordering %s before shutdown of %s because "
1151                             "node in maintenance mode",
1152                             action->uuid, pcmk__node_name(node));
1153             continue;
1154         }
1155 
1156         /* Don't touch a resource that is unmanaged or blocked, to avoid
1157          * blocking the shutdown (though if another action depends on this one,
1158          * we may still end up blocking)
1159          *
1160          * @TODO This "if" looks wrong, create a regression test for these cases
1161          */
1162         if (!pcmk_any_flags_set(action->rsc->flags,
1163                                 pcmk__rsc_managed|pcmk__rsc_blocked)) {
1164             pcmk__rsc_trace(action->rsc,
1165                             "Not ordering %s before shutdown of %s because "
1166                             "resource is unmanaged or blocked",
1167                             action->uuid, pcmk__node_name(node));
1168             continue;
1169         }
1170 
1171         pcmk__rsc_trace(action->rsc, "Ordering %s before shutdown of %s",
1172                         action->uuid, pcmk__node_name(node));
1173         pcmk__clear_action_flags(action, pcmk__action_optional);
1174         pcmk__new_ordering(action->rsc, NULL, action, NULL,
1175                            strdup(PCMK_ACTION_DO_SHUTDOWN), shutdown_op,
1176                            pcmk__ar_ordered|pcmk__ar_unrunnable_first_blocks,
1177                            node->priv->scheduler);
1178     }
1179 }
1180 
1181 /*!
1182  * \brief Find resource actions matching directly or as child
1183  *
1184  * \param[in] rsc           Resource to check
1185  * \param[in] original_key  Action key to search for (possibly referencing
1186  *                          parent of \rsc)
1187  *
1188  * \return Newly allocated list of matching actions
1189  * \note It is the caller's responsibility to free the result with g_list_free()
1190  */
1191 static GList *
1192 find_actions_by_task(const pcmk_resource_t *rsc, const char *original_key)
     /* [previous][next][first][last][top][bottom][index][help] */
1193 {
1194     // Search under given task key directly
1195     GList *list = find_actions(rsc->priv->actions, original_key, NULL);
1196 
1197     if (list == NULL) {
1198         // Search again using this resource's ID
1199         char *key = NULL;
1200         char *task = NULL;
1201         guint interval_ms = 0;
1202 
1203         CRM_CHECK(parse_op_key(original_key, NULL, &task, &interval_ms),
1204                   return NULL);
1205         key = pcmk__op_key(rsc->id, task, interval_ms);
1206         list = find_actions(rsc->priv->actions, key, NULL);
1207         free(key);
1208         free(task);
1209     }
1210     return list;
1211 }
1212 
1213 /*!
1214  * \internal
1215  * \brief Order relevant resource actions after a given action
1216  *
1217  * \param[in,out] first_action  Action to order after (or NULL if none runnable)
1218  * \param[in]     rsc           Resource whose actions should be ordered
1219  * \param[in,out] order         Ordering constraint being applied
1220  */
1221 static void
1222 order_resource_actions_after(pcmk_action_t *first_action,
     /* [previous][next][first][last][top][bottom][index][help] */
1223                              const pcmk_resource_t *rsc,
1224                              pcmk__action_relation_t *order)
1225 {
1226     GList *then_actions = NULL;
1227     uint32_t flags = pcmk__ar_none;
1228 
1229     CRM_CHECK((rsc != NULL) && (order != NULL), return);
1230 
1231     flags = order->flags;
1232     pcmk__rsc_trace(rsc, "Applying ordering %d for 'then' resource %s",
1233                     order->id, rsc->id);
1234 
1235     if (order->action2 != NULL) {
1236         then_actions = g_list_prepend(NULL, order->action2);
1237 
1238     } else {
1239         then_actions = find_actions_by_task(rsc, order->task2);
1240     }
1241 
1242     if (then_actions == NULL) {
1243         pcmk__rsc_trace(rsc, "Ignoring ordering %d: no %s actions found for %s",
1244                         order->id, order->task2, rsc->id);
1245         return;
1246     }
1247 
1248     if ((first_action != NULL) && (first_action->rsc == rsc)
1249         && pcmk_is_set(first_action->flags, pcmk__action_migration_abort)) {
1250 
1251         pcmk__rsc_trace(rsc,
1252                         "Detected dangling migration ordering (%s then %s %s)",
1253                         first_action->uuid, order->task2, rsc->id);
1254         pcmk__clear_relation_flags(flags, pcmk__ar_first_implies_then);
1255     }
1256 
1257     if ((first_action == NULL)
1258         && !pcmk_is_set(flags, pcmk__ar_first_implies_then)) {
1259 
1260         pcmk__rsc_debug(rsc,
1261                         "Ignoring ordering %d for %s: No first action found",
1262                         order->id, rsc->id);
1263         g_list_free(then_actions);
1264         return;
1265     }
1266 
1267     for (GList *iter = then_actions; iter != NULL; iter = iter->next) {
1268         pcmk_action_t *then_action_iter = (pcmk_action_t *) iter->data;
1269 
1270         if (first_action != NULL) {
1271             order_actions(first_action, then_action_iter, flags);
1272         } else {
1273             pcmk__clear_action_flags(then_action_iter, pcmk__action_runnable);
1274             // coverity[null_field] order->rsc1 can't be NULL here
1275             crm_warn("%s of %s is unrunnable because there is no %s of %s "
1276                      "to order it after", then_action_iter->task, rsc->id,
1277                      order->task1, order->rsc1->id);
1278         }
1279     }
1280 
1281     g_list_free(then_actions);
1282 }
1283 
1284 static void
1285 rsc_order_first(pcmk_resource_t *first_rsc, pcmk__action_relation_t *order)
     /* [previous][next][first][last][top][bottom][index][help] */
1286 {
1287     GList *first_actions = NULL;
1288     pcmk_action_t *first_action = order->action1;
1289     pcmk_resource_t *then_rsc = order->rsc2;
1290 
1291     pcmk__assert(first_rsc != NULL);
1292     pcmk__rsc_trace(first_rsc, "Applying ordering constraint %d (first: %s)",
1293                     order->id, first_rsc->id);
1294 
1295     if (first_action != NULL) {
1296         first_actions = g_list_prepend(NULL, first_action);
1297 
1298     } else {
1299         first_actions = find_actions_by_task(first_rsc, order->task1);
1300     }
1301 
1302     if ((first_actions == NULL) && (first_rsc == then_rsc)) {
1303         pcmk__rsc_trace(first_rsc,
1304                         "Ignoring constraint %d: first (%s for %s) not found",
1305                         order->id, order->task1, first_rsc->id);
1306 
1307     } else if (first_actions == NULL) {
1308         char *key = NULL;
1309         char *op_type = NULL;
1310         guint interval_ms = 0;
1311         enum rsc_role_e first_role;
1312 
1313         parse_op_key(order->task1, NULL, &op_type, &interval_ms);
1314         key = pcmk__op_key(first_rsc->id, op_type, interval_ms);
1315 
1316         first_role = first_rsc->priv->fns->state(first_rsc, true);
1317         if ((first_role == pcmk_role_stopped)
1318             && pcmk__str_eq(op_type, PCMK_ACTION_STOP, pcmk__str_none)) {
1319             free(key);
1320             pcmk__rsc_trace(first_rsc,
1321                             "Ignoring constraint %d: first (%s for %s) "
1322                             "not found",
1323                             order->id, order->task1, first_rsc->id);
1324 
1325         } else if ((first_role == pcmk_role_unpromoted)
1326                    && pcmk__str_eq(op_type, PCMK_ACTION_DEMOTE,
1327                                    pcmk__str_none)) {
1328             free(key);
1329             pcmk__rsc_trace(first_rsc,
1330                             "Ignoring constraint %d: first (%s for %s) "
1331                             "not found",
1332                             order->id, order->task1, first_rsc->id);
1333 
1334         } else {
1335             pcmk__rsc_trace(first_rsc,
1336                             "Creating first (%s for %s) for constraint %d ",
1337                             order->task1, first_rsc->id, order->id);
1338             first_action = custom_action(first_rsc, key, op_type, NULL, TRUE,
1339                                          first_rsc->priv->scheduler);
1340             first_actions = g_list_prepend(NULL, first_action);
1341         }
1342 
1343         free(op_type);
1344     }
1345 
1346     if (then_rsc == NULL) {
1347         if (order->action2 == NULL) {
1348             pcmk__rsc_trace(first_rsc, "Ignoring constraint %d: then not found",
1349                             order->id);
1350             return;
1351         }
1352         then_rsc = order->action2->rsc;
1353     }
1354     for (GList *iter = first_actions; iter != NULL; iter = iter->next) {
1355         first_action = iter->data;
1356 
1357         if (then_rsc == NULL) {
1358             order_actions(first_action, order->action2, order->flags);
1359 
1360         } else {
1361             order_resource_actions_after(first_action, then_rsc, order);
1362         }
1363     }
1364 
1365     g_list_free(first_actions);
1366 }
1367 
1368 // GFunc to call pcmk__block_colocation_dependents()
1369 static void
1370 block_colocation_dependents(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1371 {
1372     pcmk__block_colocation_dependents(data);
1373 }
1374 
1375 // GFunc to call pcmk__update_action_for_orderings()
1376 static void
1377 update_action_for_orderings(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1378 {
1379     pcmk__update_action_for_orderings((pcmk_action_t *) data,
1380                                       (pcmk_scheduler_t *) user_data);
1381 }
1382 
1383 /*!
1384  * \internal
1385  * \brief Apply all ordering constraints
1386  *
1387  * \param[in,out] sched  Scheduler data
1388  */
1389 void
1390 pcmk__apply_orderings(pcmk_scheduler_t *sched)
     /* [previous][next][first][last][top][bottom][index][help] */
1391 {
1392     crm_trace("Applying ordering constraints");
1393 
1394     /* Ordering constraints need to be processed in the order they were created.
1395      * rsc_order_first() and order_resource_actions_after() require the relevant
1396      * actions to already exist in some cases, but rsc_order_first() will create
1397      * the 'first' action in certain cases. Thus calling rsc_order_first() can
1398      * change the behavior of later-created orderings.
1399      *
1400      * Also, g_list_append() should be avoided for performance reasons, so we
1401      * prepend orderings when creating them and reverse the list here.
1402      *
1403      * @TODO This is brittle and should be carefully redesigned so that the
1404      * order of creation doesn't matter, and the reverse becomes unneeded.
1405      */
1406     sched->priv->ordering_constraints =
1407         g_list_reverse(sched->priv->ordering_constraints);
1408 
1409     for (GList *iter = sched->priv->ordering_constraints;
1410          iter != NULL; iter = iter->next) {
1411 
1412         pcmk__action_relation_t *order = iter->data;
1413         pcmk_resource_t *rsc = order->rsc1;
1414 
1415         if (rsc != NULL) {
1416             rsc_order_first(rsc, order);
1417             continue;
1418         }
1419 
1420         rsc = order->rsc2;
1421         if (rsc != NULL) {
1422             order_resource_actions_after(order->action1, rsc, order);
1423 
1424         } else {
1425             crm_trace("Applying ordering constraint %d (non-resource actions)",
1426                       order->id);
1427             order_actions(order->action1, order->action2, order->flags);
1428         }
1429     }
1430 
1431     g_list_foreach(sched->priv->actions, block_colocation_dependents, NULL);
1432 
1433     crm_trace("Ordering probes");
1434     pcmk__order_probes(sched);
1435 
1436     crm_trace("Updating %d actions", g_list_length(sched->priv->actions));
1437     g_list_foreach(sched->priv->actions, update_action_for_orderings, sched);
1438 
1439     pcmk__disable_invalid_orderings(sched);
1440 }
1441 
1442 /*!
1443  * \internal
1444  * \brief Order a given action after each action in a given list
1445  *
1446  * \param[in,out] after  "After" action
1447  * \param[in,out] list   List of "before" actions
1448  */
1449 void
1450 pcmk__order_after_each(pcmk_action_t *after, GList *list)
     /* [previous][next][first][last][top][bottom][index][help] */
1451 {
1452     const char *after_desc = (after->task == NULL)? after->uuid : after->task;
1453 
1454     for (GList *iter = list; iter != NULL; iter = iter->next) {
1455         pcmk_action_t *before = (pcmk_action_t *) iter->data;
1456         const char *before_desc = before->task? before->task : before->uuid;
1457 
1458         crm_debug("Ordering %s on %s before %s on %s",
1459                   before_desc, pcmk__node_name(before->node),
1460                   after_desc, pcmk__node_name(after->node));
1461         order_actions(before, after, pcmk__ar_ordered);
1462     }
1463 }
1464 
1465 /*!
1466  * \internal
1467  * \brief Order promotions and demotions for restarts of a clone or bundle
1468  *
1469  * \param[in,out] rsc  Clone or bundle to order
1470  */
1471 void
1472 pcmk__promotable_restart_ordering(pcmk_resource_t *rsc)
     /* [previous][next][first][last][top][bottom][index][help] */
1473 {
1474     // Order start and promote after all instances are stopped
1475     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
1476                                  rsc, PCMK_ACTION_START,
1477                                  pcmk__ar_ordered);
1478     pcmk__order_resource_actions(rsc, PCMK_ACTION_STOPPED,
1479                                  rsc, PCMK_ACTION_PROMOTE,
1480                                  pcmk__ar_ordered);
1481 
1482     // Order stop, start, and promote after all instances are demoted
1483     pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
1484                                  rsc, PCMK_ACTION_STOP,
1485                                  pcmk__ar_ordered);
1486     pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
1487                                  rsc, PCMK_ACTION_START,
1488                                  pcmk__ar_ordered);
1489     pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTED,
1490                                  rsc, PCMK_ACTION_PROMOTE,
1491                                  pcmk__ar_ordered);
1492 
1493     // Order promote after all instances are started
1494     pcmk__order_resource_actions(rsc, PCMK_ACTION_RUNNING,
1495                                  rsc, PCMK_ACTION_PROMOTE,
1496                                  pcmk__ar_ordered);
1497 
1498     // Order demote after all instances are demoted
1499     pcmk__order_resource_actions(rsc, PCMK_ACTION_DEMOTE,
1500                                  rsc, PCMK_ACTION_DEMOTED,
1501                                  pcmk__ar_ordered);
1502 }

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