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

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