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

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