root/lib/pacemaker/pcmk_sched_actions.c

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

DEFINITIONS

This source file includes following definitions.
  1. action_flags_for_ordering
  2. action_uuid_for_ordering
  3. action_for_ordering
  4. update
  5. update_action_for_ordering_flags
  6. pcmk__update_action_for_orderings
  7. is_primitive_action
  8. handle_asymmetric_ordering
  9. handle_restart_ordering
  10. pcmk__update_ordered_actions
  11. pcmk__log_action
  12. pcmk__new_shutdown_action
  13. add_op_digest_to_xml
  14. pcmk__create_history_xml
  15. pcmk__action_locks_rsc_to_node
  16. sort_action_id
  17. pcmk__deduplicate_action_inputs
  18. pcmk__output_actions
  19. task_for_digest
  20. only_sanitized_changed
  21. force_restart
  22. schedule_reload
  23. pcmk__check_action_config
  24. rsc_history_as_list
  25. process_rsc_history
  26. process_node_history
  27. pcmk__handle_rsc_config_changes

   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 <stdio.h>
  13 #include <sys/param.h>
  14 #include <glib.h>
  15 
  16 #include <crm/lrmd_internal.h>
  17 #include <crm/common/scheduler_internal.h>
  18 #include <pacemaker-internal.h>
  19 #include "libpacemaker_private.h"
  20 
  21 /*!
  22  * \internal
  23  * \brief Get the action flags relevant to ordering constraints
  24  *
  25  * \param[in,out] action  Action to check
  26  * \param[in]     node    Node that *other* action in the ordering is on
  27  *                        (used only for clone resource actions)
  28  *
  29  * \return Action flags that should be used for orderings
  30  */
  31 static uint32_t
  32 action_flags_for_ordering(pcmk_action_t *action, const pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34     bool runnable = false;
  35     uint32_t flags;
  36 
  37     // For non-resource actions, return the action flags
  38     if (action->rsc == NULL) {
  39         return action->flags;
  40     }
  41 
  42     /* For non-clone resources, or a clone action not assigned to a node,
  43      * return the flags as determined by the resource method without a node
  44      * specified.
  45      */
  46     flags = action->rsc->cmds->action_flags(action, NULL);
  47     if ((node == NULL) || !pcmk__is_clone(action->rsc)) {
  48         return flags;
  49     }
  50 
  51     /* Otherwise (i.e., for clone resource actions on a specific node), first
  52      * remember whether the non-node-specific action is runnable.
  53      */
  54     runnable = pcmk_is_set(flags, pcmk_action_runnable);
  55 
  56     // Then recheck the resource method with the node
  57     flags = action->rsc->cmds->action_flags(action, node);
  58 
  59     /* For clones in ordering constraints, the node-specific "runnable" doesn't
  60      * matter, just the non-node-specific setting (i.e., is the action runnable
  61      * anywhere).
  62      *
  63      * This applies only to runnable, and only for ordering constraints. This
  64      * function shouldn't be used for other types of constraints without
  65      * changes. Not very satisfying, but it's logical and appears to work well.
  66      */
  67     if (runnable && !pcmk_is_set(flags, pcmk_action_runnable)) {
  68         pcmk__set_raw_action_flags(flags, action->rsc->id,
  69                                    pcmk_action_runnable);
  70     }
  71     return flags;
  72 }
  73 
  74 /*!
  75  * \internal
  76  * \brief Get action UUID that should be used with a resource ordering
  77  *
  78  * When an action is ordered relative to an action for a collective resource
  79  * (clone, group, or bundle), it actually needs to be ordered after all
  80  * instances of the collective have completed the relevant action (for example,
  81  * given "start CLONE then start RSC", RSC must wait until all instances of
  82  * CLONE have started). Given the UUID and resource of the first action in an
  83  * ordering, this returns the UUID of the action that should actually be used
  84  * for ordering (for example, "CLONE_started_0" instead of "CLONE_start_0").
  85  *
  86  * \param[in] first_uuid    UUID of first action in ordering
  87  * \param[in] first_rsc     Resource of first action in ordering
  88  *
  89  * \return Newly allocated copy of UUID to use with ordering
  90  * \note It is the caller's responsibility to free the return value.
  91  */
  92 static char *
  93 action_uuid_for_ordering(const char *first_uuid,
     /* [previous][next][first][last][top][bottom][index][help] */
  94                          const pcmk_resource_t *first_rsc)
  95 {
  96     guint interval_ms = 0;
  97     char *uuid = NULL;
  98     char *rid = NULL;
  99     char *first_task_str = NULL;
 100     enum action_tasks first_task = pcmk_action_unspecified;
 101     enum action_tasks remapped_task = pcmk_action_unspecified;
 102 
 103     // Only non-notify actions for collective resources need remapping
 104     if ((strstr(first_uuid, PCMK_ACTION_NOTIFY) != NULL)
 105         || (first_rsc->variant < pcmk_rsc_variant_group)) {
 106         goto done;
 107     }
 108 
 109     // Only non-recurring actions need remapping
 110     CRM_ASSERT(parse_op_key(first_uuid, &rid, &first_task_str, &interval_ms));
 111     if (interval_ms > 0) {
 112         goto done;
 113     }
 114 
 115     first_task = pcmk_parse_action(first_task_str);
 116     switch (first_task) {
 117         case pcmk_action_stop:
 118         case pcmk_action_start:
 119         case pcmk_action_notify:
 120         case pcmk_action_promote:
 121         case pcmk_action_demote:
 122             remapped_task = first_task + 1;
 123             break;
 124         case pcmk_action_stopped:
 125         case pcmk_action_started:
 126         case pcmk_action_notified:
 127         case pcmk_action_promoted:
 128         case pcmk_action_demoted:
 129             remapped_task = first_task;
 130             break;
 131         case pcmk_action_monitor:
 132         case pcmk_action_shutdown:
 133         case pcmk_action_fence:
 134             break;
 135         default:
 136             crm_err("Unknown action '%s' in ordering", first_task_str);
 137             break;
 138     }
 139 
 140     if (remapped_task != pcmk_action_unspecified) {
 141         /* If a clone or bundle has notifications enabled, the ordering will be
 142          * relative to when notifications have been sent for the remapped task.
 143          */
 144         if (pcmk_is_set(first_rsc->flags, pcmk_rsc_notify)
 145             && (pcmk__is_clone(first_rsc) || pcmk__is_bundled(first_rsc))) {
 146             uuid = pcmk__notify_key(rid, "confirmed-post",
 147                                     pcmk_action_text(remapped_task));
 148         } else {
 149             uuid = pcmk__op_key(rid, pcmk_action_text(remapped_task), 0);
 150         }
 151         pcmk__rsc_trace(first_rsc,
 152                         "Remapped action UUID %s to %s for ordering purposes",
 153                         first_uuid, uuid);
 154     }
 155 
 156 done:
 157     free(first_task_str);
 158     free(rid);
 159     return (uuid != NULL)? uuid : pcmk__str_copy(first_uuid);
 160 }
 161 
 162 /*!
 163  * \internal
 164  * \brief Get actual action that should be used with an ordering
 165  *
 166  * When an action is ordered relative to an action for a collective resource
 167  * (clone, group, or bundle), it actually needs to be ordered after all
 168  * instances of the collective have completed the relevant action (for example,
 169  * given "start CLONE then start RSC", RSC must wait until all instances of
 170  * CLONE have started). Given the first action in an ordering, this returns the
 171  * the action that should actually be used for ordering (for example, the
 172  * started action instead of the start action).
 173  *
 174  * \param[in] action  First action in an ordering
 175  *
 176  * \return Actual action that should be used for the ordering
 177  */
 178 static pcmk_action_t *
 179 action_for_ordering(pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 180 {
 181     pcmk_action_t *result = action;
 182     pcmk_resource_t *rsc = action->rsc;
 183 
 184     if ((rsc != NULL) && (rsc->variant >= pcmk_rsc_variant_group)
 185         && (action->uuid != NULL)) {
 186         char *uuid = action_uuid_for_ordering(action->uuid, rsc);
 187 
 188         result = find_first_action(rsc->actions, uuid, NULL, NULL);
 189         if (result == NULL) {
 190             crm_warn("Not remapping %s to %s because %s does not have "
 191                      "remapped action", action->uuid, uuid, rsc->id);
 192             result = action;
 193         }
 194         free(uuid);
 195     }
 196     return result;
 197 }
 198 
 199 /*!
 200  * \internal
 201  * \brief Wrapper for update_ordered_actions() method for readability
 202  *
 203  * \param[in,out] rsc        Resource to call method for
 204  * \param[in,out] first      'First' action in an ordering
 205  * \param[in,out] then       'Then' action in an ordering
 206  * \param[in]     node       If not NULL, limit scope of ordering to this
 207  *                           node (only used when interleaving instances)
 208  * \param[in]     flags      Action flags for \p first for ordering purposes
 209  * \param[in]     filter     Action flags to limit scope of certain updates
 210  *                           (may include pcmk_action_optional to affect only
 211  *                           mandatory actions, and pe_action_runnable to
 212  *                           affect only runnable actions)
 213  * \param[in]     type       Group of enum pcmk__action_relation_flags to apply
 214  * \param[in,out] scheduler  Scheduler data
 215  *
 216  * \return Group of enum pcmk__updated flags indicating what was updated
 217  */
 218 static inline uint32_t
 219 update(pcmk_resource_t *rsc, pcmk_action_t *first, pcmk_action_t *then,
     /* [previous][next][first][last][top][bottom][index][help] */
 220        const pcmk_node_t *node, uint32_t flags, uint32_t filter, uint32_t type,
 221        pcmk_scheduler_t *scheduler)
 222 {
 223     return rsc->cmds->update_ordered_actions(first, then, node, flags, filter,
 224                                              type, scheduler);
 225 }
 226 
 227 /*!
 228  * \internal
 229  * \brief Update flags for ordering's actions appropriately for ordering's flags
 230  *
 231  * \param[in,out] first        First action in an ordering
 232  * \param[in,out] then         Then action in an ordering
 233  * \param[in]     first_flags  Action flags for \p first for ordering purposes
 234  * \param[in]     then_flags   Action flags for \p then for ordering purposes
 235  * \param[in,out] order        Action wrapper for \p first in ordering
 236  * \param[in,out] scheduler    Scheduler data
 237  *
 238  * \return Group of enum pcmk__updated flags
 239  */
 240 static uint32_t
 241 update_action_for_ordering_flags(pcmk_action_t *first, pcmk_action_t *then,
     /* [previous][next][first][last][top][bottom][index][help] */
 242                                  uint32_t first_flags, uint32_t then_flags,
 243                                  pcmk__related_action_t *order,
 244                                  pcmk_scheduler_t *scheduler)
 245 {
 246     uint32_t changed = pcmk__updated_none;
 247 
 248     /* The node will only be used for clones. If interleaved, node will be NULL,
 249      * otherwise the ordering scope will be limited to the node. Normally, the
 250      * whole 'then' clone should restart if 'first' is restarted, so then->node
 251      * is needed.
 252      */
 253     pcmk_node_t *node = then->node;
 254 
 255     if (pcmk_is_set(order->type, pcmk__ar_first_implies_same_node_then)) {
 256         /* For unfencing, only instances of 'then' on the same node as 'first'
 257          * (the unfencing operation) should restart, so reset node to
 258          * first->node, at which point this case is handled like a normal
 259          * pcmk__ar_first_implies_then.
 260          */
 261         pcmk__clear_relation_flags(order->type,
 262                                    pcmk__ar_first_implies_same_node_then);
 263         pcmk__set_relation_flags(order->type, pcmk__ar_first_implies_then);
 264         node = first->node;
 265         pcmk__rsc_trace(then->rsc,
 266                         "%s then %s: mapped "
 267                         "pcmk__ar_first_implies_same_node_then to "
 268                         "pcmk__ar_first_implies_then on %s",
 269                         first->uuid, then->uuid, pcmk__node_name(node));
 270     }
 271 
 272     if (pcmk_is_set(order->type, pcmk__ar_first_implies_then)) {
 273         if (then->rsc != NULL) {
 274             changed |= update(then->rsc, first, then, node,
 275                               first_flags & pcmk_action_optional,
 276                               pcmk_action_optional, pcmk__ar_first_implies_then,
 277                               scheduler);
 278         } else if (!pcmk_is_set(first_flags, pcmk_action_optional)
 279                    && pcmk_is_set(then->flags, pcmk_action_optional)) {
 280             pcmk__clear_action_flags(then, pcmk_action_optional);
 281             pcmk__set_updated_flags(changed, first, pcmk__updated_then);
 282         }
 283         pcmk__rsc_trace(then->rsc,
 284                         "%s then %s: %s after pcmk__ar_first_implies_then",
 285                         first->uuid, then->uuid,
 286                         (changed? "changed" : "unchanged"));
 287     }
 288 
 289     if (pcmk_is_set(order->type, pcmk__ar_intermediate_stop)
 290         && (then->rsc != NULL)) {
 291         enum pe_action_flags restart = pcmk_action_optional
 292                                        |pcmk_action_runnable;
 293 
 294         changed |= update(then->rsc, first, then, node, first_flags, restart,
 295                           pcmk__ar_intermediate_stop, scheduler);
 296         pcmk__rsc_trace(then->rsc,
 297                         "%s then %s: %s after pcmk__ar_intermediate_stop",
 298                         first->uuid, then->uuid,
 299                         (changed? "changed" : "unchanged"));
 300     }
 301 
 302     if (pcmk_is_set(order->type, pcmk__ar_then_implies_first)) {
 303         if (first->rsc != NULL) {
 304             changed |= update(first->rsc, first, then, node, first_flags,
 305                               pcmk_action_optional, pcmk__ar_then_implies_first,
 306                               scheduler);
 307         } else if (!pcmk_is_set(first_flags, pcmk_action_optional)
 308                    && pcmk_is_set(first->flags, pcmk_action_runnable)) {
 309             pcmk__clear_action_flags(first, pcmk_action_runnable);
 310             pcmk__set_updated_flags(changed, first, pcmk__updated_first);
 311         }
 312         pcmk__rsc_trace(then->rsc,
 313                         "%s then %s: %s after pcmk__ar_then_implies_first",
 314                         first->uuid, then->uuid,
 315                         (changed? "changed" : "unchanged"));
 316     }
 317 
 318     if (pcmk_is_set(order->type, pcmk__ar_promoted_then_implies_first)) {
 319         if (then->rsc != NULL) {
 320             changed |= update(then->rsc, first, then, node,
 321                               first_flags & pcmk_action_optional,
 322                               pcmk_action_optional,
 323                               pcmk__ar_promoted_then_implies_first, scheduler);
 324         }
 325         pcmk__rsc_trace(then->rsc,
 326                         "%s then %s: %s after "
 327                         "pcmk__ar_promoted_then_implies_first",
 328                         first->uuid, then->uuid,
 329                         (changed? "changed" : "unchanged"));
 330     }
 331 
 332     if (pcmk_is_set(order->type, pcmk__ar_min_runnable)) {
 333         if (then->rsc != NULL) {
 334             changed |= update(then->rsc, first, then, node, first_flags,
 335                               pcmk_action_runnable, pcmk__ar_min_runnable,
 336                               scheduler);
 337 
 338         } else if (pcmk_is_set(first_flags, pcmk_action_runnable)) {
 339             // We have another runnable instance of "first"
 340             then->runnable_before++;
 341 
 342             /* Mark "then" as runnable if it requires a certain number of
 343              * "before" instances to be runnable, and they now are.
 344              */
 345             if ((then->runnable_before >= then->required_runnable_before)
 346                 && !pcmk_is_set(then->flags, pcmk_action_runnable)) {
 347 
 348                 pcmk__set_action_flags(then, pcmk_action_runnable);
 349                 pcmk__set_updated_flags(changed, first, pcmk__updated_then);
 350             }
 351         }
 352         pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_min_runnable",
 353                         first->uuid, then->uuid,
 354                         (changed? "changed" : "unchanged"));
 355     }
 356 
 357     if (pcmk_is_set(order->type, pcmk__ar_nested_remote_probe)
 358         && (then->rsc != NULL)) {
 359 
 360         if (!pcmk_is_set(first_flags, pcmk_action_runnable)
 361             && (first->rsc != NULL) && (first->rsc->running_on != NULL)) {
 362 
 363             pcmk__rsc_trace(then->rsc,
 364                             "%s then %s: ignoring because first is stopping",
 365                             first->uuid, then->uuid);
 366             order->type = (enum pe_ordering) pcmk__ar_none;
 367         } else {
 368             changed |= update(then->rsc, first, then, node, first_flags,
 369                               pcmk_action_runnable,
 370                               pcmk__ar_unrunnable_first_blocks, scheduler);
 371         }
 372         pcmk__rsc_trace(then->rsc,
 373                         "%s then %s: %s after pcmk__ar_nested_remote_probe",
 374                         first->uuid, then->uuid,
 375                         (changed? "changed" : "unchanged"));
 376     }
 377 
 378     if (pcmk_is_set(order->type, pcmk__ar_unrunnable_first_blocks)) {
 379         if (then->rsc != NULL) {
 380             changed |= update(then->rsc, first, then, node, first_flags,
 381                               pcmk_action_runnable,
 382                               pcmk__ar_unrunnable_first_blocks, scheduler);
 383 
 384         } else if (!pcmk_is_set(first_flags, pcmk_action_runnable)
 385                    && pcmk_is_set(then->flags, pcmk_action_runnable)) {
 386 
 387             pcmk__clear_action_flags(then, pcmk_action_runnable);
 388             pcmk__set_updated_flags(changed, first, pcmk__updated_then);
 389         }
 390         pcmk__rsc_trace(then->rsc,
 391                         "%s then %s: %s after pcmk__ar_unrunnable_first_blocks",
 392                         first->uuid, then->uuid,
 393                         (changed? "changed" : "unchanged"));
 394     }
 395 
 396     if (pcmk_is_set(order->type, pcmk__ar_unmigratable_then_blocks)) {
 397         if (then->rsc != NULL) {
 398             changed |= update(then->rsc, first, then, node, first_flags,
 399                               pcmk_action_optional,
 400                               pcmk__ar_unmigratable_then_blocks, scheduler);
 401         }
 402         pcmk__rsc_trace(then->rsc,
 403                         "%s then %s: %s after "
 404                         "pcmk__ar_unmigratable_then_blocks",
 405                         first->uuid, then->uuid,
 406                         (changed? "changed" : "unchanged"));
 407     }
 408 
 409     if (pcmk_is_set(order->type, pcmk__ar_first_else_then)) {
 410         if (then->rsc != NULL) {
 411             changed |= update(then->rsc, first, then, node, first_flags,
 412                               pcmk_action_optional, pcmk__ar_first_else_then,
 413                               scheduler);
 414         }
 415         pcmk__rsc_trace(then->rsc,
 416                         "%s then %s: %s after pcmk__ar_first_else_then",
 417                         first->uuid, then->uuid,
 418                         (changed? "changed" : "unchanged"));
 419     }
 420 
 421     if (pcmk_is_set(order->type, pcmk__ar_ordered)) {
 422         if (then->rsc != NULL) {
 423             changed |= update(then->rsc, first, then, node, first_flags,
 424                               pcmk_action_runnable, pcmk__ar_ordered,
 425                               scheduler);
 426         }
 427         pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_ordered",
 428                         first->uuid, then->uuid,
 429                         (changed? "changed" : "unchanged"));
 430     }
 431 
 432     if (pcmk_is_set(order->type, pcmk__ar_asymmetric)) {
 433         if (then->rsc != NULL) {
 434             changed |= update(then->rsc, first, then, node, first_flags,
 435                               pcmk_action_runnable, pcmk__ar_asymmetric,
 436                               scheduler);
 437         }
 438         pcmk__rsc_trace(then->rsc, "%s then %s: %s after pcmk__ar_asymmetric",
 439                         first->uuid, then->uuid,
 440                         (changed? "changed" : "unchanged"));
 441     }
 442 
 443     if (pcmk_is_set(first->flags, pcmk_action_runnable)
 444         && pcmk_is_set(order->type, pcmk__ar_first_implies_then_graphed)
 445         && !pcmk_is_set(first_flags, pcmk_action_optional)) {
 446 
 447         pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required",
 448                         then->uuid, first->uuid);
 449         pcmk__set_action_flags(then, pcmk_action_always_in_graph);
 450         // Don't bother marking 'then' as changed just for this
 451     }
 452 
 453     if (pcmk_is_set(order->type, pcmk__ar_then_implies_first_graphed)
 454         && !pcmk_is_set(then_flags, pcmk_action_optional)) {
 455 
 456         pcmk__rsc_trace(then->rsc, "%s will be in graph because %s is required",
 457                         first->uuid, then->uuid);
 458         pcmk__set_action_flags(first, pcmk_action_always_in_graph);
 459         // Don't bother marking 'first' as changed just for this
 460     }
 461 
 462     if (pcmk_any_flags_set(order->type, pcmk__ar_first_implies_then
 463                                         |pcmk__ar_then_implies_first
 464                                         |pcmk__ar_intermediate_stop)
 465         && (first->rsc != NULL)
 466         && !pcmk_is_set(first->rsc->flags, pcmk_rsc_managed)
 467         && pcmk_is_set(first->rsc->flags, pcmk_rsc_blocked)
 468         && !pcmk_is_set(first->flags, pcmk_action_runnable)
 469         && pcmk__str_eq(first->task, PCMK_ACTION_STOP, pcmk__str_none)) {
 470 
 471         if (pcmk_is_set(then->flags, pcmk_action_runnable)) {
 472             pcmk__clear_action_flags(then, pcmk_action_runnable);
 473             pcmk__set_updated_flags(changed, first, pcmk__updated_then);
 474         }
 475         pcmk__rsc_trace(then->rsc,
 476                         "%s then %s: %s after checking whether first "
 477                         "is blocked, unmanaged, unrunnable stop",
 478                         first->uuid, then->uuid,
 479                         (changed? "changed" : "unchanged"));
 480     }
 481 
 482     return changed;
 483 }
 484 
 485 // Convenience macros for logging action properties
 486 
 487 #define action_type_str(flags) \
 488     (pcmk_is_set((flags), pcmk_action_pseudo)? "pseudo-action" : "action")
 489 
 490 #define action_optional_str(flags) \
 491     (pcmk_is_set((flags), pcmk_action_optional)? "optional" : "required")
 492 
 493 #define action_runnable_str(flags) \
 494     (pcmk_is_set((flags), pcmk_action_runnable)? "runnable" : "unrunnable")
 495 
 496 #define action_node_str(a) \
 497     (((a)->node == NULL)? "no node" : (a)->node->details->uname)
 498 
 499 /*!
 500  * \internal
 501  * \brief Update an action's flags for all orderings where it is "then"
 502  *
 503  * \param[in,out] then       Action to update
 504  * \param[in,out] scheduler  Scheduler data
 505  */
 506 void
 507 pcmk__update_action_for_orderings(pcmk_action_t *then,
     /* [previous][next][first][last][top][bottom][index][help] */
 508                                   pcmk_scheduler_t *scheduler)
 509 {
 510     GList *lpc = NULL;
 511     uint32_t changed = pcmk__updated_none;
 512     int last_flags = then->flags;
 513 
 514     pcmk__rsc_trace(then->rsc, "Updating %s %s (%s %s) on %s",
 515                     action_type_str(then->flags), then->uuid,
 516                     action_optional_str(then->flags),
 517                     action_runnable_str(then->flags), action_node_str(then));
 518 
 519     if (pcmk_is_set(then->flags, pcmk_action_min_runnable)) {
 520         /* Initialize current known "runnable before" actions. As
 521          * update_action_for_ordering_flags() is called for each of then's
 522          * before actions, this number will increment as runnable 'first'
 523          * actions are encountered.
 524          */
 525         then->runnable_before = 0;
 526 
 527         if (then->required_runnable_before == 0) {
 528             /* @COMPAT This ordering constraint uses the deprecated
 529              * PCMK_XA_REQUIRE_ALL=PCMK_VALUE_FALSE attribute. Treat it like
 530              * PCMK_META_CLONE_MIN=1.
 531              */
 532             then->required_runnable_before = 1;
 533         }
 534 
 535         /* The pcmk__ar_min_runnable clause of
 536          * update_action_for_ordering_flags() (called below)
 537          * will reset runnable if appropriate.
 538          */
 539         pcmk__clear_action_flags(then, pcmk_action_runnable);
 540     }
 541 
 542     for (lpc = then->actions_before; lpc != NULL; lpc = lpc->next) {
 543         pcmk__related_action_t *other = lpc->data;
 544         pcmk_action_t *first = other->action;
 545 
 546         pcmk_node_t *then_node = then->node;
 547         pcmk_node_t *first_node = first->node;
 548 
 549         if ((first->rsc != NULL)
 550             && pcmk__is_group(first->rsc)
 551             && pcmk__str_eq(first->task, PCMK_ACTION_START, pcmk__str_none)) {
 552 
 553             first_node = first->rsc->fns->location(first->rsc, NULL, FALSE);
 554             if (first_node != NULL) {
 555                 pcmk__rsc_trace(first->rsc, "Found %s for 'first' %s",
 556                                 pcmk__node_name(first_node), first->uuid);
 557             }
 558         }
 559 
 560         if (pcmk__is_group(then->rsc)
 561             && pcmk__str_eq(then->task, PCMK_ACTION_START, pcmk__str_none)) {
 562 
 563             then_node = then->rsc->fns->location(then->rsc, NULL, FALSE);
 564             if (then_node != NULL) {
 565                 pcmk__rsc_trace(then->rsc, "Found %s for 'then' %s",
 566                                 pcmk__node_name(then_node), then->uuid);
 567             }
 568         }
 569 
 570         // Disable constraint if it only applies when on same node, but isn't
 571         if (pcmk_is_set(other->type, pcmk__ar_if_on_same_node)
 572             && (first_node != NULL) && (then_node != NULL)
 573             && !pcmk__same_node(first_node, then_node)) {
 574 
 575             pcmk__rsc_trace(then->rsc,
 576                             "Disabled ordering %s on %s then %s on %s: "
 577                             "not same node",
 578                             other->action->uuid, pcmk__node_name(first_node),
 579                             then->uuid, pcmk__node_name(then_node));
 580             other->type = (enum pe_ordering) pcmk__ar_none;
 581             continue;
 582         }
 583 
 584         pcmk__clear_updated_flags(changed, then, pcmk__updated_first);
 585 
 586         if ((first->rsc != NULL)
 587             && pcmk_is_set(other->type, pcmk__ar_then_cancels_first)
 588             && !pcmk_is_set(then->flags, pcmk_action_optional)) {
 589 
 590             /* 'then' is required, so we must abandon 'first'
 591              * (e.g. a required stop cancels any agent reload).
 592              */
 593             pcmk__set_action_flags(other->action, pcmk_action_optional);
 594             if (!strcmp(first->task, PCMK_ACTION_RELOAD_AGENT)) {
 595                 pcmk__clear_rsc_flags(first->rsc, pcmk_rsc_reload);
 596             }
 597         }
 598 
 599         if ((first->rsc != NULL) && (then->rsc != NULL)
 600             && (first->rsc != then->rsc) && !is_parent(then->rsc, first->rsc)) {
 601             first = action_for_ordering(first);
 602         }
 603         if (first != other->action) {
 604             pcmk__rsc_trace(then->rsc, "Ordering %s after %s instead of %s",
 605                             then->uuid, first->uuid, other->action->uuid);
 606         }
 607 
 608         pcmk__rsc_trace(then->rsc,
 609                         "%s (%#.6x) then %s (%#.6x): type=%#.6x node=%s",
 610                         first->uuid, first->flags, then->uuid, then->flags,
 611                         other->type, action_node_str(first));
 612 
 613         if (first == other->action) {
 614             /* 'first' was not remapped (e.g. from 'start' to 'running'), which
 615              * could mean it is a non-resource action, a primitive resource
 616              * action, or already expanded.
 617              */
 618             uint32_t first_flags, then_flags;
 619 
 620             first_flags = action_flags_for_ordering(first, then_node);
 621             then_flags = action_flags_for_ordering(then, first_node);
 622 
 623             changed |= update_action_for_ordering_flags(first, then,
 624                                                         first_flags, then_flags,
 625                                                         other, scheduler);
 626 
 627             /* 'first' was for a complex resource (clone, group, etc),
 628              * create a new dependency if necessary
 629              */
 630         } else if (order_actions(first, then, other->type)) {
 631             /* This was the first time 'first' and 'then' were associated,
 632              * start again to get the new actions_before list
 633              */
 634             pcmk__set_updated_flags(changed, then, pcmk__updated_then);
 635             pcmk__rsc_trace(then->rsc,
 636                             "Disabled ordering %s then %s in favor of %s "
 637                             "then %s",
 638                             other->action->uuid, then->uuid, first->uuid,
 639                             then->uuid);
 640             other->type = (enum pe_ordering) pcmk__ar_none;
 641         }
 642 
 643 
 644         if (pcmk_is_set(changed, pcmk__updated_first)) {
 645             crm_trace("Re-processing %s and its 'after' actions "
 646                       "because it changed", first->uuid);
 647             for (GList *lpc2 = first->actions_after; lpc2 != NULL;
 648                  lpc2 = lpc2->next) {
 649                 pcmk__related_action_t *other = lpc2->data;
 650 
 651                 pcmk__update_action_for_orderings(other->action, scheduler);
 652             }
 653             pcmk__update_action_for_orderings(first, scheduler);
 654         }
 655     }
 656 
 657     if (pcmk_is_set(then->flags, pcmk_action_min_runnable)) {
 658         if (last_flags == then->flags) {
 659             pcmk__clear_updated_flags(changed, then, pcmk__updated_then);
 660         } else {
 661             pcmk__set_updated_flags(changed, then, pcmk__updated_then);
 662         }
 663     }
 664 
 665     if (pcmk_is_set(changed, pcmk__updated_then)) {
 666         crm_trace("Re-processing %s and its 'after' actions because it changed",
 667                   then->uuid);
 668         if (pcmk_is_set(last_flags, pcmk_action_runnable)
 669             && !pcmk_is_set(then->flags, pcmk_action_runnable)) {
 670             pcmk__block_colocation_dependents(then);
 671         }
 672         pcmk__update_action_for_orderings(then, scheduler);
 673         for (lpc = then->actions_after; lpc != NULL; lpc = lpc->next) {
 674             pcmk__related_action_t *other = lpc->data;
 675 
 676             pcmk__update_action_for_orderings(other->action, scheduler);
 677         }
 678     }
 679 }
 680 
 681 static inline bool
 682 is_primitive_action(const pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
 683 {
 684     return (action != NULL) && pcmk__is_primitive(action->rsc);
 685 }
 686 
 687 /*!
 688  * \internal
 689  * \brief Clear a single action flag and set reason text
 690  *
 691  * \param[in,out] action  Action whose flag should be cleared
 692  * \param[in]     flag    Action flag that should be cleared
 693  * \param[in]     reason  Action that is the reason why flag is being cleared
 694  */
 695 #define clear_action_flag_because(action, flag, reason) do {                \
 696         if (pcmk_is_set((action)->flags, (flag))) {                         \
 697             pcmk__clear_action_flags(action, flag);                         \
 698             if ((action)->rsc != (reason)->rsc) {                           \
 699                 char *reason_text = pe__action2reason((reason), (flag));    \
 700                 pe_action_set_reason((action), reason_text, false);         \
 701                 free(reason_text);                                          \
 702             }                                                               \
 703         }                                                                   \
 704     } while (0)
 705 
 706 /*!
 707  * \internal
 708  * \brief Update actions in an asymmetric ordering
 709  *
 710  * If the "first" action in an asymmetric ordering is unrunnable, make the
 711  * "second" action unrunnable as well, if appropriate.
 712  *
 713  * \param[in]     first  'First' action in an asymmetric ordering
 714  * \param[in,out] then   'Then' action in an asymmetric ordering
 715  */
 716 static void
 717 handle_asymmetric_ordering(const pcmk_action_t *first, pcmk_action_t *then)
     /* [previous][next][first][last][top][bottom][index][help] */
 718 {
 719     /* Only resource actions after an unrunnable 'first' action need updates for
 720      * asymmetric ordering.
 721      */
 722     if ((then->rsc == NULL)
 723         || pcmk_is_set(first->flags, pcmk_action_runnable)) {
 724         return;
 725     }
 726 
 727     // Certain optional 'then' actions are unaffected by unrunnable 'first'
 728     if (pcmk_is_set(then->flags, pcmk_action_optional)) {
 729         enum rsc_role_e then_rsc_role = then->rsc->fns->state(then->rsc, TRUE);
 730 
 731         if ((then_rsc_role == pcmk_role_stopped)
 732             && pcmk__str_eq(then->task, PCMK_ACTION_STOP, pcmk__str_none)) {
 733             /* If 'then' should stop after 'first' but is already stopped, the
 734              * ordering is irrelevant.
 735              */
 736             return;
 737         } else if ((then_rsc_role >= pcmk_role_started)
 738             && pcmk__str_eq(then->task, PCMK_ACTION_START, pcmk__str_none)
 739             && pe__rsc_running_on_only(then->rsc, then->node)) {
 740             /* Similarly if 'then' should start after 'first' but is already
 741              * started on a single node.
 742              */
 743             return;
 744         }
 745     }
 746 
 747     // 'First' can't run, so 'then' can't either
 748     clear_action_flag_because(then, pcmk_action_optional, first);
 749     clear_action_flag_because(then, pcmk_action_runnable, first);
 750 }
 751 
 752 /*!
 753  * \internal
 754  * \brief Set action bits appropriately when pe_restart_order is used
 755  *
 756  * \param[in,out] first   'First' action in an ordering with pe_restart_order
 757  * \param[in,out] then    'Then' action in an ordering with pe_restart_order
 758  * \param[in]     filter  What action flags to care about
 759  *
 760  * \note pe_restart_order is set for "stop resource before starting it" and
 761  *       "stop later group member before stopping earlier group member"
 762  */
 763 static void
 764 handle_restart_ordering(pcmk_action_t *first, pcmk_action_t *then,
     /* [previous][next][first][last][top][bottom][index][help] */
 765                         uint32_t filter)
 766 {
 767     const char *reason = NULL;
 768 
 769     CRM_ASSERT(is_primitive_action(first));
 770     CRM_ASSERT(is_primitive_action(then));
 771 
 772     // We need to update the action in two cases:
 773 
 774     // ... if 'then' is required
 775     if (pcmk_is_set(filter, pcmk_action_optional)
 776         && !pcmk_is_set(then->flags, pcmk_action_optional)) {
 777         reason = "restart";
 778     }
 779 
 780     /* ... if 'then' is unrunnable action on same resource (if a resource
 781      * should restart but can't start, we still want to stop)
 782      */
 783     if (pcmk_is_set(filter, pcmk_action_runnable)
 784         && !pcmk_is_set(then->flags, pcmk_action_runnable)
 785         && pcmk_is_set(then->rsc->flags, pcmk_rsc_managed)
 786         && (first->rsc == then->rsc)) {
 787         reason = "stop";
 788     }
 789 
 790     if (reason == NULL) {
 791         return;
 792     }
 793 
 794     pcmk__rsc_trace(first->rsc, "Handling %s -> %s for %s",
 795                     first->uuid, then->uuid, reason);
 796 
 797     // Make 'first' required if it is runnable
 798     if (pcmk_is_set(first->flags, pcmk_action_runnable)) {
 799         clear_action_flag_because(first, pcmk_action_optional, then);
 800     }
 801 
 802     // Make 'first' required if 'then' is required
 803     if (!pcmk_is_set(then->flags, pcmk_action_optional)) {
 804         clear_action_flag_because(first, pcmk_action_optional, then);
 805     }
 806 
 807     // Make 'first' unmigratable if 'then' is unmigratable
 808     if (!pcmk_is_set(then->flags, pcmk_action_migratable)) {
 809         clear_action_flag_because(first, pcmk_action_migratable, then);
 810     }
 811 
 812     // Make 'then' unrunnable if 'first' is required but unrunnable
 813     if (!pcmk_is_set(first->flags, pcmk_action_optional)
 814         && !pcmk_is_set(first->flags, pcmk_action_runnable)) {
 815         clear_action_flag_because(then, pcmk_action_runnable, first);
 816     }
 817 }
 818 
 819 /*!
 820  * \internal
 821  * \brief Update two actions according to an ordering between them
 822  *
 823  * Given information about an ordering of two actions, update the actions' flags
 824  * (and runnable_before members if appropriate) as appropriate for the ordering.
 825  * Effects may cascade to other orderings involving the actions as well.
 826  *
 827  * \param[in,out] first      'First' action in an ordering
 828  * \param[in,out] then       'Then' action in an ordering
 829  * \param[in]     node       If not NULL, limit scope of ordering to this node
 830  *                           (ignored)
 831  * \param[in]     flags      Action flags for \p first for ordering purposes
 832  * \param[in]     filter     Action flags to limit scope of certain updates (may
 833  *                           include pcmk_action_optional to affect only
 834  *                           mandatory actions, and pcmk_action_runnable to
 835  *                           affect only runnable actions)
 836  * \param[in]     type       Group of enum pcmk__action_relation_flags to apply
 837  * \param[in,out] scheduler  Scheduler data
 838  *
 839  * \return Group of enum pcmk__updated flags indicating what was updated
 840  */
 841 uint32_t
 842 pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then,
     /* [previous][next][first][last][top][bottom][index][help] */
 843                              const pcmk_node_t *node, uint32_t flags,
 844                              uint32_t filter, uint32_t type,
 845                              pcmk_scheduler_t *scheduler)
 846 {
 847     uint32_t changed = pcmk__updated_none;
 848     uint32_t then_flags = 0U;
 849     uint32_t first_flags = 0U;
 850 
 851     CRM_ASSERT((first != NULL) && (then != NULL) && (scheduler != NULL));
 852 
 853     then_flags = then->flags;
 854     first_flags = first->flags;
 855     if (pcmk_is_set(type, pcmk__ar_asymmetric)) {
 856         handle_asymmetric_ordering(first, then);
 857     }
 858 
 859     if (pcmk_is_set(type, pcmk__ar_then_implies_first)
 860         && !pcmk_is_set(then_flags, pcmk_action_optional)) {
 861         // Then is required, and implies first should be, too
 862 
 863         if (pcmk_is_set(filter, pcmk_action_optional)
 864             && !pcmk_is_set(flags, pcmk_action_optional)
 865             && pcmk_is_set(first_flags, pcmk_action_optional)) {
 866             clear_action_flag_because(first, pcmk_action_optional, then);
 867         }
 868 
 869         if (pcmk_is_set(flags, pcmk_action_migratable)
 870             && !pcmk_is_set(then->flags, pcmk_action_migratable)) {
 871             clear_action_flag_because(first, pcmk_action_migratable, then);
 872         }
 873     }
 874 
 875     if (pcmk_is_set(type, pcmk__ar_promoted_then_implies_first)
 876         && (then->rsc != NULL) && (then->rsc->role == pcmk_role_promoted)
 877         && pcmk_is_set(filter, pcmk_action_optional)
 878         && !pcmk_is_set(then->flags, pcmk_action_optional)) {
 879 
 880         clear_action_flag_because(first, pcmk_action_optional, then);
 881 
 882         if (pcmk_is_set(first->flags, pcmk_action_migratable)
 883             && !pcmk_is_set(then->flags, pcmk_action_migratable)) {
 884             clear_action_flag_because(first, pcmk_action_migratable, then);
 885         }
 886     }
 887 
 888     if (pcmk_is_set(type, pcmk__ar_unmigratable_then_blocks)
 889         && pcmk_is_set(filter, pcmk_action_optional)) {
 890 
 891         if (!pcmk_all_flags_set(then->flags, pcmk_action_migratable
 892                                              |pcmk_action_runnable)) {
 893             clear_action_flag_because(first, pcmk_action_runnable, then);
 894         }
 895 
 896         if (!pcmk_is_set(then->flags, pcmk_action_optional)) {
 897             clear_action_flag_because(first, pcmk_action_optional, then);
 898         }
 899     }
 900 
 901     if (pcmk_is_set(type, pcmk__ar_first_else_then)
 902         && pcmk_is_set(filter, pcmk_action_optional)
 903         && !pcmk_is_set(first->flags, pcmk_action_runnable)) {
 904 
 905         clear_action_flag_because(then, pcmk_action_migratable, first);
 906         pcmk__clear_action_flags(then, pcmk_action_pseudo);
 907     }
 908 
 909     if (pcmk_is_set(type, pcmk__ar_unrunnable_first_blocks)
 910         && pcmk_is_set(filter, pcmk_action_runnable)
 911         && pcmk_is_set(then->flags, pcmk_action_runnable)
 912         && !pcmk_is_set(flags, pcmk_action_runnable)) {
 913 
 914         clear_action_flag_because(then, pcmk_action_runnable, first);
 915         clear_action_flag_because(then, pcmk_action_migratable, first);
 916     }
 917 
 918     if (pcmk_is_set(type, pcmk__ar_first_implies_then)
 919         && pcmk_is_set(filter, pcmk_action_optional)
 920         && pcmk_is_set(then->flags, pcmk_action_optional)
 921         && !pcmk_is_set(flags, pcmk_action_optional)
 922         && !pcmk_is_set(first->flags, pcmk_action_migratable)) {
 923 
 924         clear_action_flag_because(then, pcmk_action_optional, first);
 925     }
 926 
 927     if (pcmk_is_set(type, pcmk__ar_intermediate_stop)) {
 928         handle_restart_ordering(first, then, filter);
 929     }
 930 
 931     if (then_flags != then->flags) {
 932         pcmk__set_updated_flags(changed, first, pcmk__updated_then);
 933         pcmk__rsc_trace(then->rsc,
 934                         "%s on %s: flags are now %#.6x (was %#.6x) "
 935                         "because of 'first' %s (%#.6x)",
 936                         then->uuid, pcmk__node_name(then->node),
 937                         then->flags, then_flags, first->uuid, first->flags);
 938 
 939         if ((then->rsc != NULL) && (then->rsc->parent != NULL)) {
 940             // Required to handle "X_stop then X_start" for cloned groups
 941             pcmk__update_action_for_orderings(then, scheduler);
 942         }
 943     }
 944 
 945     if (first_flags != first->flags) {
 946         pcmk__set_updated_flags(changed, first, pcmk__updated_first);
 947         pcmk__rsc_trace(first->rsc,
 948                         "%s on %s: flags are now %#.6x (was %#.6x) "
 949                         "because of 'then' %s (%#.6x)",
 950                         first->uuid, pcmk__node_name(first->node),
 951                         first->flags, first_flags, then->uuid, then->flags);
 952     }
 953 
 954     return changed;
 955 }
 956 
 957 /*!
 958  * \internal
 959  * \brief Trace-log an action (optionally with its dependent actions)
 960  *
 961  * \param[in] pre_text  If not NULL, prefix the log with this plus ": "
 962  * \param[in] action    Action to log
 963  * \param[in] details   If true, recursively log dependent actions
 964  */
 965 void
 966 pcmk__log_action(const char *pre_text, const pcmk_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 967                  bool details)
 968 {
 969     const char *node_uname = NULL;
 970     const char *node_uuid = NULL;
 971     const char *desc = NULL;
 972 
 973     CRM_CHECK(action != NULL, return);
 974 
 975     if (!pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 976         if (action->node != NULL) {
 977             node_uname = action->node->details->uname;
 978             node_uuid = action->node->details->id;
 979         } else {
 980             node_uname = "<none>";
 981         }
 982     }
 983 
 984     switch (pcmk_parse_action(action->task)) {
 985         case pcmk_action_fence:
 986         case pcmk_action_shutdown:
 987             if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 988                 desc = "Pseudo ";
 989             } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
 990                 desc = "Optional ";
 991             } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
 992                 desc = "!!Non-Startable!! ";
 993             } else {
 994                desc = "(Provisional) ";
 995             }
 996             crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
 997                       ((pre_text == NULL)? "" : pre_text),
 998                       ((pre_text == NULL)? "" : ": "),
 999                       desc, action->id, action->uuid,
1000                       (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1001                       (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1002                       (node_uuid? ")" : ""));
1003             break;
1004         default:
1005             if (pcmk_is_set(action->flags, pcmk_action_optional)) {
1006                 desc = "Optional ";
1007             } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
1008                 desc = "Pseudo ";
1009             } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
1010                 desc = "!!Non-Startable!! ";
1011             } else {
1012                desc = "(Provisional) ";
1013             }
1014             crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
1015                       ((pre_text == NULL)? "" : pre_text),
1016                       ((pre_text == NULL)? "" : ": "),
1017                       desc, action->id, action->uuid,
1018                       (action->rsc? action->rsc->id : "<none>"),
1019                       (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1020                       (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1021                       (node_uuid? ")" : ""));
1022             break;
1023     }
1024 
1025     if (details) {
1026         const GList *iter = NULL;
1027         const pcmk__related_action_t *other = NULL;
1028 
1029         crm_trace("\t\t====== Preceding Actions");
1030         for (iter = action->actions_before; iter != NULL; iter = iter->next) {
1031             other = (const pcmk__related_action_t *) iter->data;
1032             pcmk__log_action("\t\t", other->action, false);
1033         }
1034         crm_trace("\t\t====== Subsequent Actions");
1035         for (iter = action->actions_after; iter != NULL; iter = iter->next) {
1036             other = (const pcmk__related_action_t *) iter->data;
1037             pcmk__log_action("\t\t", other->action, false);
1038         }
1039         crm_trace("\t\t====== End");
1040 
1041     } else {
1042         crm_trace("\t\t(before=%d, after=%d)",
1043                   g_list_length(action->actions_before),
1044                   g_list_length(action->actions_after));
1045     }
1046 }
1047 
1048 /*!
1049  * \internal
1050  * \brief Create a new shutdown action for a node
1051  *
1052  * \param[in,out] node  Node being shut down
1053  *
1054  * \return Newly created shutdown action for \p node
1055  */
1056 pcmk_action_t *
1057 pcmk__new_shutdown_action(pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1058 {
1059     char *shutdown_id = NULL;
1060     pcmk_action_t *shutdown_op = NULL;
1061 
1062     CRM_ASSERT(node != NULL);
1063 
1064     shutdown_id = crm_strdup_printf("%s-%s", PCMK_ACTION_DO_SHUTDOWN,
1065                                     node->details->uname);
1066 
1067     shutdown_op = custom_action(NULL, shutdown_id, PCMK_ACTION_DO_SHUTDOWN,
1068                                 node, FALSE, node->details->data_set);
1069 
1070     pcmk__order_stops_before_shutdown(node, shutdown_op);
1071     pcmk__insert_meta(shutdown_op, PCMK__META_OP_NO_WAIT, PCMK_VALUE_TRUE);
1072     return shutdown_op;
1073 }
1074 
1075 /*!
1076  * \internal
1077  * \brief Calculate and add an operation digest to XML
1078  *
1079  * Calculate an operation digest, which enables us to later determine when a
1080  * restart is needed due to the resource's parameters being changed, and add it
1081  * to given XML.
1082  *
1083  * \param[in]     op      Operation result from executor
1084  * \param[in,out] update  XML to add digest to
1085  */
1086 static void
1087 add_op_digest_to_xml(const lrmd_event_data_t *op, xmlNode *update)
     /* [previous][next][first][last][top][bottom][index][help] */
1088 {
1089     char *digest = NULL;
1090     xmlNode *args_xml = NULL;
1091 
1092     if (op->params == NULL) {
1093         return;
1094     }
1095     args_xml = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
1096     g_hash_table_foreach(op->params, hash2field, args_xml);
1097     pcmk__filter_op_for_digest(args_xml);
1098     digest = calculate_operation_digest(args_xml, NULL);
1099     crm_xml_add(update, PCMK__XA_OP_DIGEST, digest);
1100     free_xml(args_xml);
1101     free(digest);
1102 }
1103 
1104 #define FAKE_TE_ID     "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
1105 
1106 /*!
1107  * \internal
1108  * \brief Create XML for resource operation history update
1109  *
1110  * \param[in,out] parent          Parent XML node to add to
1111  * \param[in,out] op              Operation event data
1112  * \param[in]     caller_version  DC feature set
1113  * \param[in]     target_rc       Expected result of operation
1114  * \param[in]     node            Name of node on which operation was performed
1115  * \param[in]     origin          Arbitrary description of update source
1116  *
1117  * \return Newly created XML node for history update
1118  */
1119 xmlNode *
1120 pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op,
     /* [previous][next][first][last][top][bottom][index][help] */
1121                          const char *caller_version, int target_rc,
1122                          const char *node, const char *origin)
1123 {
1124     char *key = NULL;
1125     char *magic = NULL;
1126     char *op_id = NULL;
1127     char *op_id_additional = NULL;
1128     char *local_user_data = NULL;
1129     const char *exit_reason = NULL;
1130 
1131     xmlNode *xml_op = NULL;
1132     const char *task = NULL;
1133 
1134     CRM_CHECK(op != NULL, return NULL);
1135     crm_trace("Creating history XML for %s-interval %s action for %s on %s "
1136               "(DC version: %s, origin: %s)",
1137               pcmk__readable_interval(op->interval_ms), op->op_type, op->rsc_id,
1138               ((node == NULL)? "no node" : node), caller_version, origin);
1139 
1140     task = op->op_type;
1141 
1142     /* Record a successful agent reload as a start, and a failed one as a
1143      * monitor, to make life easier for the scheduler when determining the
1144      * current state.
1145      *
1146      * @COMPAT We should check "reload" here only if the operation was for a
1147      * pre-OCF-1.1 resource agent, but we don't know that here, and we should
1148      * only ever get results for actions scheduled by us, so we can reasonably
1149      * assume any "reload" is actually a pre-1.1 agent reload.
1150      */
1151     if (pcmk__str_any_of(task, PCMK_ACTION_RELOAD, PCMK_ACTION_RELOAD_AGENT,
1152                          NULL)) {
1153         if (op->op_status == PCMK_EXEC_DONE) {
1154             task = PCMK_ACTION_START;
1155         } else {
1156             task = PCMK_ACTION_MONITOR;
1157         }
1158     }
1159 
1160     key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
1161     if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
1162         const char *n_type = crm_meta_value(op->params, "notify_type");
1163         const char *n_task = crm_meta_value(op->params, "notify_operation");
1164 
1165         CRM_LOG_ASSERT(n_type != NULL);
1166         CRM_LOG_ASSERT(n_task != NULL);
1167         op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
1168 
1169         if (op->op_status != PCMK_EXEC_PENDING) {
1170             /* Ignore notify errors.
1171              *
1172              * @TODO It might be better to keep the correct result here, and
1173              * ignore it in process_graph_event().
1174              */
1175             lrmd__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
1176         }
1177 
1178     /* Migration history is preserved separately, which usually matters for
1179      * multiple nodes and is important for future cluster transitions.
1180      */
1181     } else if (pcmk__str_any_of(op->op_type, PCMK_ACTION_MIGRATE_TO,
1182                                 PCMK_ACTION_MIGRATE_FROM, NULL)) {
1183         op_id = strdup(key);
1184 
1185     } else if (did_rsc_op_fail(op, target_rc)) {
1186         op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
1187         if (op->interval_ms == 0) {
1188             /* Ensure 'last' gets updated, in case PCMK_META_RECORD_PENDING is
1189              * true
1190              */
1191             op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
1192         }
1193         exit_reason = op->exit_reason;
1194 
1195     } else if (op->interval_ms > 0) {
1196         op_id = strdup(key);
1197 
1198     } else {
1199         op_id = pcmk__op_key(op->rsc_id, "last", 0);
1200     }
1201 
1202   again:
1203     xml_op = pcmk__xe_first_child(parent, PCMK__XE_LRM_RSC_OP, PCMK_XA_ID,
1204                                   op_id);
1205     if (xml_op == NULL) {
1206         xml_op = pcmk__xe_create(parent, PCMK__XE_LRM_RSC_OP);
1207     }
1208 
1209     if (op->user_data == NULL) {
1210         crm_debug("Generating fake transition key for: " PCMK__OP_FMT
1211                   " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
1212                   op->call_id, origin);
1213         local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
1214                                                FAKE_TE_ID);
1215         op->user_data = local_user_data;
1216     }
1217 
1218     if (magic == NULL) {
1219         magic = crm_strdup_printf("%d:%d;%s", op->op_status, op->rc,
1220                                   (const char *) op->user_data);
1221     }
1222 
1223     crm_xml_add(xml_op, PCMK_XA_ID, op_id);
1224     crm_xml_add(xml_op, PCMK__XA_OPERATION_KEY, key);
1225     crm_xml_add(xml_op, PCMK_XA_OPERATION, task);
1226     crm_xml_add(xml_op, PCMK_XA_CRM_DEBUG_ORIGIN, origin);
1227     crm_xml_add(xml_op, PCMK_XA_CRM_FEATURE_SET, caller_version);
1228     crm_xml_add(xml_op, PCMK__XA_TRANSITION_KEY, op->user_data);
1229     crm_xml_add(xml_op, PCMK__XA_TRANSITION_MAGIC, magic);
1230     crm_xml_add(xml_op, PCMK_XA_EXIT_REASON, pcmk__s(exit_reason, ""));
1231     crm_xml_add(xml_op, PCMK__META_ON_NODE, node); // For context during triage
1232 
1233     crm_xml_add_int(xml_op, PCMK__XA_CALL_ID, op->call_id);
1234     crm_xml_add_int(xml_op, PCMK__XA_RC_CODE, op->rc);
1235     crm_xml_add_int(xml_op, PCMK__XA_OP_STATUS, op->op_status);
1236     crm_xml_add_ms(xml_op, PCMK_META_INTERVAL, op->interval_ms);
1237 
1238     if (compare_version("2.1", caller_version) <= 0) {
1239         if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
1240             crm_trace("Timing data (" PCMK__OP_FMT
1241                       "): last=%u change=%u exec=%u queue=%u",
1242                       op->rsc_id, op->op_type, op->interval_ms,
1243                       op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
1244 
1245             if ((op->interval_ms != 0) && (op->t_rcchange != 0)) {
1246                 // Recurring ops may have changed rc after initial run
1247                 crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE,
1248                                (long long) op->t_rcchange);
1249             } else {
1250                 crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE,
1251                                (long long) op->t_run);
1252             }
1253 
1254             crm_xml_add_int(xml_op, PCMK_XA_EXEC_TIME, op->exec_time);
1255             crm_xml_add_int(xml_op, PCMK_XA_QUEUE_TIME, op->queue_time);
1256         }
1257     }
1258 
1259     if (pcmk__str_any_of(op->op_type, PCMK_ACTION_MIGRATE_TO,
1260                          PCMK_ACTION_MIGRATE_FROM, NULL)) {
1261         /* Record PCMK__META_MIGRATE_SOURCE and PCMK__META_MIGRATE_TARGET always
1262          * for migrate ops.
1263          */
1264         const char *name = PCMK__META_MIGRATE_SOURCE;
1265 
1266         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1267 
1268         name = PCMK__META_MIGRATE_TARGET;
1269         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1270     }
1271 
1272     add_op_digest_to_xml(op, xml_op);
1273 
1274     if (op_id_additional) {
1275         free(op_id);
1276         op_id = op_id_additional;
1277         op_id_additional = NULL;
1278         goto again;
1279     }
1280 
1281     if (local_user_data) {
1282         free(local_user_data);
1283         op->user_data = NULL;
1284     }
1285     free(magic);
1286     free(op_id);
1287     free(key);
1288     return xml_op;
1289 }
1290 
1291 /*!
1292  * \internal
1293  * \brief Check whether an action shutdown-locks a resource to a node
1294  *
1295  * If the PCMK_OPT_SHUTDOWN_LOCK cluster property is set, resources will not be
1296  * recovered on a different node if cleanly stopped, and may start only on that
1297  * same node. This function checks whether that applies to a given action, so
1298  * that the transition graph can be marked appropriately.
1299  *
1300  * \param[in] action  Action to check
1301  *
1302  * \return true if \p action locks its resource to the action's node,
1303  *         otherwise false
1304  */
1305 bool
1306 pcmk__action_locks_rsc_to_node(const pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1307 {
1308     // Only resource actions taking place on resource's lock node are locked
1309     if ((action == NULL) || (action->rsc == NULL)
1310         || !pcmk__same_node(action->node, action->rsc->lock_node)) {
1311         return false;
1312     }
1313 
1314     /* During shutdown, only stops are locked (otherwise, another action such as
1315      * a demote would cause the controller to clear the lock)
1316      */
1317     if (action->node->details->shutdown && (action->task != NULL)
1318         && (strcmp(action->task, PCMK_ACTION_STOP) != 0)) {
1319         return false;
1320     }
1321 
1322     return true;
1323 }
1324 
1325 /* lowest to highest */
1326 static gint
1327 sort_action_id(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1328 {
1329     const pcmk__related_action_t *action_wrapper2 = a;
1330     const pcmk__related_action_t *action_wrapper1 = b;
1331 
1332     if (a == NULL) {
1333         return 1;
1334     }
1335     if (b == NULL) {
1336         return -1;
1337     }
1338     if (action_wrapper1->action->id < action_wrapper2->action->id) {
1339         return 1;
1340     }
1341     if (action_wrapper1->action->id > action_wrapper2->action->id) {
1342         return -1;
1343     }
1344     return 0;
1345 }
1346 
1347 /*!
1348  * \internal
1349  * \brief Remove any duplicate action inputs, merging action flags
1350  *
1351  * \param[in,out] action  Action whose inputs should be checked
1352  */
1353 void
1354 pcmk__deduplicate_action_inputs(pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1355 {
1356     GList *item = NULL;
1357     GList *next = NULL;
1358     pcmk__related_action_t *last_input = NULL;
1359 
1360     action->actions_before = g_list_sort(action->actions_before,
1361                                          sort_action_id);
1362     for (item = action->actions_before; item != NULL; item = next) {
1363         pcmk__related_action_t *input = item->data;
1364 
1365         next = item->next;
1366         if ((last_input != NULL)
1367             && (input->action->id == last_input->action->id)) {
1368             crm_trace("Input %s (%d) duplicate skipped for action %s (%d)",
1369                       input->action->uuid, input->action->id,
1370                       action->uuid, action->id);
1371 
1372             /* For the purposes of scheduling, the ordering flags no longer
1373              * matter, but crm_simulate looks at certain ones when creating a
1374              * dot graph. Combining the flags is sufficient for that purpose.
1375              */
1376             last_input->type |= input->type;
1377             if (input->state == pe_link_dumped) {
1378                 last_input->state = pe_link_dumped;
1379             }
1380 
1381             free(item->data);
1382             action->actions_before = g_list_delete_link(action->actions_before,
1383                                                         item);
1384         } else {
1385             last_input = input;
1386             input->state = pe_link_not_dumped;
1387         }
1388     }
1389 }
1390 
1391 /*!
1392  * \internal
1393  * \brief Output all scheduled actions
1394  *
1395  * \param[in,out] scheduler  Scheduler data
1396  */
1397 void
1398 pcmk__output_actions(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
1399 {
1400     pcmk__output_t *out = scheduler->priv;
1401 
1402     // Output node (non-resource) actions
1403     for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) {
1404         char *node_name = NULL;
1405         char *task = NULL;
1406         pcmk_action_t *action = (pcmk_action_t *) iter->data;
1407 
1408         if (action->rsc != NULL) {
1409             continue; // Resource actions will be output later
1410 
1411         } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
1412             continue; // This action was not scheduled
1413         }
1414 
1415         if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN,
1416                          pcmk__str_none)) {
1417             task = strdup("Shutdown");
1418 
1419         } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
1420                                 pcmk__str_none)) {
1421             const char *op = g_hash_table_lookup(action->meta,
1422                                                  PCMK__META_STONITH_ACTION);
1423 
1424             task = crm_strdup_printf("Fence (%s)", op);
1425 
1426         } else {
1427             continue; // Don't display other node action types
1428         }
1429 
1430         if (pcmk__is_guest_or_bundle_node(action->node)) {
1431             const pcmk_resource_t *remote = action->node->details->remote_rsc;
1432 
1433             node_name = crm_strdup_printf("%s (resource: %s)",
1434                                           pcmk__node_name(action->node),
1435                                           remote->container->id);
1436         } else if (action->node != NULL) {
1437             node_name = crm_strdup_printf("%s", pcmk__node_name(action->node));
1438         }
1439 
1440         out->message(out, "node-action", task, node_name, action->reason);
1441 
1442         free(node_name);
1443         free(task);
1444     }
1445 
1446     // Output resource actions
1447     for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
1448         pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1449 
1450         rsc->cmds->output_actions(rsc);
1451     }
1452 }
1453 
1454 /*!
1455  * \internal
1456  * \brief Get action name needed to compare digest for configuration changes
1457  *
1458  * \param[in] task         Action name from history
1459  * \param[in] interval_ms  Action interval (in milliseconds)
1460  *
1461  * \return Action name whose digest should be compared
1462  */
1463 static const char *
1464 task_for_digest(const char *task, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
1465 {
1466     /* Certain actions need to be compared against the parameters used to start
1467      * the resource.
1468      */
1469     if ((interval_ms == 0)
1470         && pcmk__str_any_of(task, PCMK_ACTION_MONITOR, PCMK_ACTION_MIGRATE_FROM,
1471                             PCMK_ACTION_PROMOTE, NULL)) {
1472         task = PCMK_ACTION_START;
1473     }
1474     return task;
1475 }
1476 
1477 /*!
1478  * \internal
1479  * \brief Check whether only sanitized parameters to an action changed
1480  *
1481  * When collecting CIB files for troubleshooting, crm_report will mask
1482  * sensitive resource parameters. If simulations were run using that, affected
1483  * resources would appear to need a restart, which would complicate
1484  * troubleshooting. To avoid that, we save a "secure digest" of non-sensitive
1485  * parameters. This function used that digest to check whether only masked
1486  * parameters are different.
1487  *
1488  * \param[in] xml_op       Resource history entry with secure digest
1489  * \param[in] digest_data  Operation digest information being compared
1490  * \param[in] scheduler    Scheduler data
1491  *
1492  * \return true if only sanitized parameters changed, otherwise false
1493  */
1494 static bool
1495 only_sanitized_changed(const xmlNode *xml_op,
     /* [previous][next][first][last][top][bottom][index][help] */
1496                        const pcmk__op_digest_t *digest_data,
1497                        const pcmk_scheduler_t *scheduler)
1498 {
1499     const char *digest_secure = NULL;
1500 
1501     if (!pcmk_is_set(scheduler->flags, pcmk_sched_sanitized)) {
1502         // The scheduler is not being run as a simulation
1503         return false;
1504     }
1505 
1506     digest_secure = crm_element_value(xml_op, PCMK__XA_OP_SECURE_DIGEST);
1507 
1508     return (digest_data->rc != pcmk__digest_match) && (digest_secure != NULL)
1509            && (digest_data->digest_secure_calc != NULL)
1510            && (strcmp(digest_data->digest_secure_calc, digest_secure) == 0);
1511 }
1512 
1513 /*!
1514  * \internal
1515  * \brief Force a restart due to a configuration change
1516  *
1517  * \param[in,out] rsc          Resource that action is for
1518  * \param[in]     task         Name of action whose configuration changed
1519  * \param[in]     interval_ms  Action interval (in milliseconds)
1520  * \param[in,out] node         Node where resource should be restarted
1521  */
1522 static void
1523 force_restart(pcmk_resource_t *rsc, const char *task, guint interval_ms,
     /* [previous][next][first][last][top][bottom][index][help] */
1524               pcmk_node_t *node)
1525 {
1526     char *key = pcmk__op_key(rsc->id, task, interval_ms);
1527     pcmk_action_t *required = custom_action(rsc, key, task, NULL, FALSE,
1528                                             rsc->cluster);
1529 
1530     pe_action_set_reason(required, "resource definition change", true);
1531     trigger_unfencing(rsc, node, "Device parameters changed", NULL,
1532                       rsc->cluster);
1533 }
1534 
1535 /*!
1536  * \internal
1537  * \brief Schedule a reload of a resource on a node
1538  *
1539  * \param[in,out] data       Resource to reload
1540  * \param[in]     user_data  Where resource should be reloaded
1541  */
1542 static void
1543 schedule_reload(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1544 {
1545     pcmk_resource_t *rsc = data;
1546     const pcmk_node_t *node = user_data;
1547     pcmk_action_t *reload = NULL;
1548 
1549     // For collective resources, just call recursively for children
1550     if (rsc->variant > pcmk_rsc_variant_primitive) {
1551         g_list_foreach(rsc->children, schedule_reload, user_data);
1552         return;
1553     }
1554 
1555     // Skip the reload in certain situations
1556     if ((node == NULL)
1557         || !pcmk_is_set(rsc->flags, pcmk_rsc_managed)
1558         || pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
1559         pcmk__rsc_trace(rsc, "Skip reload of %s:%s%s %s",
1560                         rsc->id,
1561                         pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " unmanaged",
1562                         pcmk_is_set(rsc->flags, pcmk_rsc_failed)? " failed" : "",
1563                         (node == NULL)? "inactive" : node->details->uname);
1564         return;
1565     }
1566 
1567     /* If a resource's configuration changed while a start was pending,
1568      * force a full restart instead of a reload.
1569      */
1570     if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) {
1571         pcmk__rsc_trace(rsc,
1572                         "%s: preventing agent reload because start pending",
1573                         rsc->id);
1574         custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
1575                       rsc->cluster);
1576         return;
1577     }
1578 
1579     // Schedule the reload
1580     pcmk__set_rsc_flags(rsc, pcmk_rsc_reload);
1581     reload = custom_action(rsc, reload_key(rsc), PCMK_ACTION_RELOAD_AGENT, node,
1582                            FALSE, rsc->cluster);
1583     pe_action_set_reason(reload, "resource definition change", FALSE);
1584 
1585     // Set orderings so that a required stop or demote cancels the reload
1586     pcmk__new_ordering(NULL, NULL, reload, rsc, stop_key(rsc), NULL,
1587                        pcmk__ar_ordered|pcmk__ar_then_cancels_first,
1588                        rsc->cluster);
1589     pcmk__new_ordering(NULL, NULL, reload, rsc, demote_key(rsc), NULL,
1590                        pcmk__ar_ordered|pcmk__ar_then_cancels_first,
1591                        rsc->cluster);
1592 }
1593 
1594 /*!
1595  * \internal
1596  * \brief Handle any configuration change for an action
1597  *
1598  * Given an action from resource history, if the resource's configuration
1599  * changed since the action was done, schedule any actions needed (restart,
1600  * reload, unfencing, rescheduling recurring actions, etc.).
1601  *
1602  * \param[in,out] rsc     Resource that action is for
1603  * \param[in,out] node    Node that action was on
1604  * \param[in]     xml_op  Action XML from resource history
1605  *
1606  * \return true if action configuration changed, otherwise false
1607  */
1608 bool
1609 pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1610                           const xmlNode *xml_op)
1611 {
1612     guint interval_ms = 0;
1613     const char *task = NULL;
1614     const pcmk__op_digest_t *digest_data = NULL;
1615 
1616     CRM_CHECK((rsc != NULL) && (node != NULL) && (xml_op != NULL),
1617               return false);
1618 
1619     task = crm_element_value(xml_op, PCMK_XA_OPERATION);
1620     CRM_CHECK(task != NULL, return false);
1621 
1622     crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
1623 
1624     // If this is a recurring action, check whether it has been orphaned
1625     if (interval_ms > 0) {
1626         if (pcmk__find_action_config(rsc, task, interval_ms, false) != NULL) {
1627             pcmk__rsc_trace(rsc,
1628                             "%s-interval %s for %s on %s is in configuration",
1629                             pcmk__readable_interval(interval_ms), task, rsc->id,
1630                             pcmk__node_name(node));
1631         } else if (pcmk_is_set(rsc->cluster->flags,
1632                                pcmk_sched_cancel_removed_actions)) {
1633             pcmk__schedule_cancel(rsc,
1634                                   crm_element_value(xml_op, PCMK__XA_CALL_ID),
1635                                   task, interval_ms, node, "orphan");
1636             return true;
1637         } else {
1638             pcmk__rsc_debug(rsc, "%s-interval %s for %s on %s is orphaned",
1639                             pcmk__readable_interval(interval_ms), task, rsc->id,
1640                             pcmk__node_name(node));
1641             return true;
1642         }
1643     }
1644 
1645     crm_trace("Checking %s-interval %s for %s on %s for configuration changes",
1646               pcmk__readable_interval(interval_ms), task, rsc->id,
1647               pcmk__node_name(node));
1648     task = task_for_digest(task, interval_ms);
1649     digest_data = rsc_action_digest_cmp(rsc, xml_op, node, rsc->cluster);
1650 
1651     if (only_sanitized_changed(xml_op, digest_data, rsc->cluster)) {
1652         if (!pcmk__is_daemon && (rsc->cluster->priv != NULL)) {
1653             pcmk__output_t *out = rsc->cluster->priv;
1654 
1655             out->info(out,
1656                       "Only 'private' parameters to %s-interval %s for %s "
1657                       "on %s changed: %s",
1658                       pcmk__readable_interval(interval_ms), task, rsc->id,
1659                       pcmk__node_name(node),
1660                       crm_element_value(xml_op, PCMK__XA_TRANSITION_MAGIC));
1661         }
1662         return false;
1663     }
1664 
1665     switch (digest_data->rc) {
1666         case pcmk__digest_restart:
1667             crm_log_xml_debug(digest_data->params_restart, "params:restart");
1668             force_restart(rsc, task, interval_ms, node);
1669             return true;
1670 
1671         case pcmk__digest_unknown:
1672         case pcmk__digest_mismatch:
1673             // Changes that can potentially be handled by an agent reload
1674 
1675             if (interval_ms > 0) {
1676                 /* Recurring actions aren't reloaded per se, they are just
1677                  * re-scheduled so the next run uses the new parameters.
1678                  * The old instance will be cancelled automatically.
1679                  */
1680                 crm_log_xml_debug(digest_data->params_all, "params:reschedule");
1681                 pcmk__reschedule_recurring(rsc, task, interval_ms, node);
1682 
1683             } else if (crm_element_value(xml_op,
1684                                          PCMK__XA_OP_RESTART_DIGEST) != NULL) {
1685                 // Agent supports reload, so use it
1686                 trigger_unfencing(rsc, node,
1687                                   "Device parameters changed (reload)", NULL,
1688                                   rsc->cluster);
1689                 crm_log_xml_debug(digest_data->params_all, "params:reload");
1690                 schedule_reload((gpointer) rsc, (gpointer) node);
1691 
1692             } else {
1693                 pcmk__rsc_trace(rsc,
1694                                 "Restarting %s "
1695                                 "because agent doesn't support reload",
1696                                 rsc->id);
1697                 crm_log_xml_debug(digest_data->params_restart,
1698                                   "params:restart");
1699                 force_restart(rsc, task, interval_ms, node);
1700             }
1701             return true;
1702 
1703         default:
1704             break;
1705     }
1706     return false;
1707 }
1708 
1709 /*!
1710  * \internal
1711  * \brief Create a list of resource's action history entries, sorted by call ID
1712  *
1713  * \param[in]  rsc_entry    Resource's \c PCMK__XE_LRM_RSC_OP status XML
1714  * \param[out] start_index  Where to store index of start-like action, if any
1715  * \param[out] stop_index   Where to store index of stop action, if any
1716  */
1717 static GList *
1718 rsc_history_as_list(const xmlNode *rsc_entry, int *start_index, int *stop_index)
     /* [previous][next][first][last][top][bottom][index][help] */
1719 {
1720     GList *ops = NULL;
1721 
1722     for (xmlNode *rsc_op = pcmk__xe_first_child(rsc_entry, PCMK__XE_LRM_RSC_OP,
1723                                                 NULL, NULL);
1724          rsc_op != NULL; rsc_op = pcmk__xe_next_same(rsc_op)) {
1725 
1726         ops = g_list_prepend(ops, rsc_op);
1727     }
1728     ops = g_list_sort(ops, sort_op_by_callid);
1729     calculate_active_ops(ops, start_index, stop_index);
1730     return ops;
1731 }
1732 
1733 /*!
1734  * \internal
1735  * \brief Process a resource's action history from the CIB status
1736  *
1737  * Given a resource's action history, if the resource's configuration
1738  * changed since the actions were done, schedule any actions needed (restart,
1739  * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1740  * (This also cancels recurring actions for maintenance mode, which is not
1741  * entirely related but convenient to do here.)
1742  *
1743  * \param[in]     rsc_entry  Resource's \c PCMK__XE_LRM_RSC_OP status XML
1744  * \param[in,out] rsc        Resource whose history is being processed
1745  * \param[in,out] node       Node whose history is being processed
1746  */
1747 static void
1748 process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1749                     pcmk_node_t *node)
1750 {
1751     int offset = -1;
1752     int stop_index = 0;
1753     int start_index = 0;
1754     GList *sorted_op_list = NULL;
1755 
1756     if (pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
1757         if (pcmk__is_anonymous_clone(pe__const_top_resource(rsc, false))) {
1758             pcmk__rsc_trace(rsc,
1759                             "Skipping configuration check "
1760                             "for orphaned clone instance %s",
1761                             rsc->id);
1762         } else {
1763             pcmk__rsc_trace(rsc,
1764                             "Skipping configuration check and scheduling "
1765                             "clean-up for orphaned resource %s", rsc->id);
1766             pcmk__schedule_cleanup(rsc, node, false);
1767         }
1768         return;
1769     }
1770 
1771     if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
1772         if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, false)) {
1773             pcmk__schedule_cleanup(rsc, node, false);
1774         }
1775         pcmk__rsc_trace(rsc,
1776                         "Skipping configuration check for %s "
1777                         "because no longer active on %s",
1778                         rsc->id, pcmk__node_name(node));
1779         return;
1780     }
1781 
1782     pcmk__rsc_trace(rsc, "Checking for configuration changes for %s on %s",
1783                     rsc->id, pcmk__node_name(node));
1784 
1785     if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, true)) {
1786         pcmk__schedule_cleanup(rsc, node, false);
1787     }
1788 
1789     sorted_op_list = rsc_history_as_list(rsc_entry, &start_index, &stop_index);
1790     if (start_index < stop_index) {
1791         return; // Resource is stopped
1792     }
1793 
1794     for (GList *iter = sorted_op_list; iter != NULL; iter = iter->next) {
1795         xmlNode *rsc_op = (xmlNode *) iter->data;
1796         const char *task = NULL;
1797         guint interval_ms = 0;
1798 
1799         if (++offset < start_index) {
1800             // Skip actions that happened before a start
1801             continue;
1802         }
1803 
1804         task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
1805         crm_element_value_ms(rsc_op, PCMK_META_INTERVAL, &interval_ms);
1806 
1807         if ((interval_ms > 0)
1808             && (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)
1809                 || node->details->maintenance)) {
1810             // Maintenance mode cancels recurring operations
1811             pcmk__schedule_cancel(rsc,
1812                                   crm_element_value(rsc_op, PCMK__XA_CALL_ID),
1813                                   task, interval_ms, node, "maintenance mode");
1814 
1815         } else if ((interval_ms > 0)
1816                    || pcmk__strcase_any_of(task, PCMK_ACTION_MONITOR,
1817                                            PCMK_ACTION_START,
1818                                            PCMK_ACTION_PROMOTE,
1819                                            PCMK_ACTION_MIGRATE_FROM, NULL)) {
1820             /* If a resource operation failed, and the operation's definition
1821              * has changed, clear any fail count so they can be retried fresh.
1822              */
1823 
1824             if (pe__bundle_needs_remote_name(rsc)) {
1825                 /* We haven't assigned resources to nodes yet, so if the
1826                  * REMOTE_CONTAINER_HACK is used, we may calculate the digest
1827                  * based on the literal "#uname" value rather than the properly
1828                  * substituted value. That would mistakenly make the action
1829                  * definition appear to have been changed. Defer the check until
1830                  * later in this case.
1831                  */
1832                 pe__add_param_check(rsc_op, rsc, node, pcmk__check_active,
1833                                     rsc->cluster);
1834 
1835             } else if (pcmk__check_action_config(rsc, node, rsc_op)
1836                        && (pe_get_failcount(node, rsc, NULL, pcmk__fc_effective,
1837                                             NULL) != 0)) {
1838                 pe__clear_failcount(rsc, node, "action definition changed",
1839                                     rsc->cluster);
1840             }
1841         }
1842     }
1843     g_list_free(sorted_op_list);
1844 }
1845 
1846 /*!
1847  * \internal
1848  * \brief Process a node's action history from the CIB status
1849  *
1850  * Given a node's resource history, if the resource's configuration changed
1851  * since the actions were done, schedule any actions needed (restart,
1852  * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1853  * (This also cancels recurring actions for maintenance mode, which is not
1854  * entirely related but convenient to do here.)
1855  *
1856  * \param[in,out] node      Node whose history is being processed
1857  * \param[in]     lrm_rscs  Node's \c PCMK__XE_LRM_RESOURCES from CIB status XML
1858  */
1859 static void
1860 process_node_history(pcmk_node_t *node, const xmlNode *lrm_rscs)
     /* [previous][next][first][last][top][bottom][index][help] */
1861 {
1862     crm_trace("Processing node history for %s", pcmk__node_name(node));
1863     for (const xmlNode *rsc_entry = pcmk__xe_first_child(lrm_rscs,
1864                                                          PCMK__XE_LRM_RESOURCE,
1865                                                          NULL, NULL);
1866          rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) {
1867 
1868         if (rsc_entry->children != NULL) {
1869             GList *result = pcmk__rscs_matching_id(pcmk__xe_id(rsc_entry),
1870                                                    node->details->data_set);
1871 
1872             for (GList *iter = result; iter != NULL; iter = iter->next) {
1873                 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1874 
1875                 if (pcmk__is_primitive(rsc)) {
1876                     process_rsc_history(rsc_entry, rsc, node);
1877                 }
1878             }
1879             g_list_free(result);
1880         }
1881     }
1882 }
1883 
1884 // XPath to find a node's resource history
1885 #define XPATH_NODE_HISTORY "/" PCMK_XE_CIB "/" PCMK_XE_STATUS   \
1886                            "/" PCMK__XE_NODE_STATE              \
1887                            "[@" PCMK_XA_UNAME "='%s']"          \
1888                            "/" PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES
1889 
1890 /*!
1891  * \internal
1892  * \brief Process any resource configuration changes in the CIB status
1893  *
1894  * Go through all nodes' resource history, and if a resource's configuration
1895  * changed since its actions were done, schedule any actions needed (restart,
1896  * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1897  * (This also cancels recurring actions for maintenance mode, which is not
1898  * entirely related but convenient to do here.)
1899  *
1900  * \param[in,out] scheduler  Scheduler data
1901  */
1902 void
1903 pcmk__handle_rsc_config_changes(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
1904 {
1905     crm_trace("Check resource and action configuration for changes");
1906 
1907     /* Rather than iterate through the status section, iterate through the nodes
1908      * and search for the appropriate status subsection for each. This skips
1909      * orphaned nodes and lets us eliminate some cases before searching the XML.
1910      */
1911     for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) {
1912         pcmk_node_t *node = (pcmk_node_t *) iter->data;
1913 
1914         /* Don't bother checking actions for a node that can't run actions ...
1915          * unless it's in maintenance mode, in which case we still need to
1916          * cancel any existing recurring monitors.
1917          */
1918         if (node->details->maintenance
1919             || pcmk__node_available(node, false, false)) {
1920 
1921             char *xpath = NULL;
1922             xmlNode *history = NULL;
1923 
1924             xpath = crm_strdup_printf(XPATH_NODE_HISTORY, node->details->uname);
1925             history = get_xpath_object(xpath, scheduler->input, LOG_NEVER);
1926             free(xpath);
1927 
1928             process_node_history(node, history);
1929         }
1930     }
1931 }

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