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     pcmk__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     pcmk__assert(is_primitive_action(first) && is_primitive_action(then));
 770 
 771     // We need to update the action in two cases:
 772 
 773     // ... if 'then' is required
 774     if (pcmk_is_set(filter, pcmk_action_optional)
 775         && !pcmk_is_set(then->flags, pcmk_action_optional)) {
 776         reason = "restart";
 777     }
 778 
 779     /* ... if 'then' is unrunnable action on same resource (if a resource
 780      * should restart but can't start, we still want to stop)
 781      */
 782     if (pcmk_is_set(filter, pcmk_action_runnable)
 783         && !pcmk_is_set(then->flags, pcmk_action_runnable)
 784         && pcmk_is_set(then->rsc->flags, pcmk_rsc_managed)
 785         && (first->rsc == then->rsc)) {
 786         reason = "stop";
 787     }
 788 
 789     if (reason == NULL) {
 790         return;
 791     }
 792 
 793     pcmk__rsc_trace(first->rsc, "Handling %s -> %s for %s",
 794                     first->uuid, then->uuid, reason);
 795 
 796     // Make 'first' required if it is runnable
 797     if (pcmk_is_set(first->flags, pcmk_action_runnable)) {
 798         clear_action_flag_because(first, pcmk_action_optional, then);
 799     }
 800 
 801     // Make 'first' required if 'then' is required
 802     if (!pcmk_is_set(then->flags, pcmk_action_optional)) {
 803         clear_action_flag_because(first, pcmk_action_optional, then);
 804     }
 805 
 806     // Make 'first' unmigratable if 'then' is unmigratable
 807     if (!pcmk_is_set(then->flags, pcmk_action_migratable)) {
 808         clear_action_flag_because(first, pcmk_action_migratable, then);
 809     }
 810 
 811     // Make 'then' unrunnable if 'first' is required but unrunnable
 812     if (!pcmk_is_set(first->flags, pcmk_action_optional)
 813         && !pcmk_is_set(first->flags, pcmk_action_runnable)) {
 814         clear_action_flag_because(then, pcmk_action_runnable, first);
 815     }
 816 }
 817 
 818 /*!
 819  * \internal
 820  * \brief Update two actions according to an ordering between them
 821  *
 822  * Given information about an ordering of two actions, update the actions' flags
 823  * (and runnable_before members if appropriate) as appropriate for the ordering.
 824  * Effects may cascade to other orderings involving the actions as well.
 825  *
 826  * \param[in,out] first      'First' action in an ordering
 827  * \param[in,out] then       'Then' action in an ordering
 828  * \param[in]     node       If not NULL, limit scope of ordering to this node
 829  *                           (ignored)
 830  * \param[in]     flags      Action flags for \p first for ordering purposes
 831  * \param[in]     filter     Action flags to limit scope of certain updates (may
 832  *                           include pcmk_action_optional to affect only
 833  *                           mandatory actions, and pcmk_action_runnable to
 834  *                           affect only runnable actions)
 835  * \param[in]     type       Group of enum pcmk__action_relation_flags to apply
 836  * \param[in,out] scheduler  Scheduler data
 837  *
 838  * \return Group of enum pcmk__updated flags indicating what was updated
 839  */
 840 uint32_t
 841 pcmk__update_ordered_actions(pcmk_action_t *first, pcmk_action_t *then,
     /* [previous][next][first][last][top][bottom][index][help] */
 842                              const pcmk_node_t *node, uint32_t flags,
 843                              uint32_t filter, uint32_t type,
 844                              pcmk_scheduler_t *scheduler)
 845 {
 846     uint32_t changed = pcmk__updated_none;
 847     uint32_t then_flags = 0U;
 848     uint32_t first_flags = 0U;
 849 
 850     pcmk__assert((first != NULL) && (then != NULL) && (scheduler != NULL));
 851 
 852     then_flags = then->flags;
 853     first_flags = first->flags;
 854     if (pcmk_is_set(type, pcmk__ar_asymmetric)) {
 855         handle_asymmetric_ordering(first, then);
 856     }
 857 
 858     if (pcmk_is_set(type, pcmk__ar_then_implies_first)
 859         && !pcmk_is_set(then_flags, pcmk_action_optional)) {
 860         // Then is required, and implies first should be, too
 861 
 862         if (pcmk_is_set(filter, pcmk_action_optional)
 863             && !pcmk_is_set(flags, pcmk_action_optional)
 864             && pcmk_is_set(first_flags, pcmk_action_optional)) {
 865             clear_action_flag_because(first, pcmk_action_optional, then);
 866         }
 867 
 868         if (pcmk_is_set(flags, pcmk_action_migratable)
 869             && !pcmk_is_set(then->flags, pcmk_action_migratable)) {
 870             clear_action_flag_because(first, pcmk_action_migratable, then);
 871         }
 872     }
 873 
 874     if (pcmk_is_set(type, pcmk__ar_promoted_then_implies_first)
 875         && (then->rsc != NULL) && (then->rsc->role == pcmk_role_promoted)
 876         && pcmk_is_set(filter, pcmk_action_optional)
 877         && !pcmk_is_set(then->flags, pcmk_action_optional)) {
 878 
 879         clear_action_flag_because(first, pcmk_action_optional, then);
 880 
 881         if (pcmk_is_set(first->flags, pcmk_action_migratable)
 882             && !pcmk_is_set(then->flags, pcmk_action_migratable)) {
 883             clear_action_flag_because(first, pcmk_action_migratable, then);
 884         }
 885     }
 886 
 887     if (pcmk_is_set(type, pcmk__ar_unmigratable_then_blocks)
 888         && pcmk_is_set(filter, pcmk_action_optional)) {
 889 
 890         if (!pcmk_all_flags_set(then->flags, pcmk_action_migratable
 891                                              |pcmk_action_runnable)) {
 892             clear_action_flag_because(first, pcmk_action_runnable, then);
 893         }
 894 
 895         if (!pcmk_is_set(then->flags, pcmk_action_optional)) {
 896             clear_action_flag_because(first, pcmk_action_optional, then);
 897         }
 898     }
 899 
 900     if (pcmk_is_set(type, pcmk__ar_first_else_then)
 901         && pcmk_is_set(filter, pcmk_action_optional)
 902         && !pcmk_is_set(first->flags, pcmk_action_runnable)) {
 903 
 904         clear_action_flag_because(then, pcmk_action_migratable, first);
 905         pcmk__clear_action_flags(then, pcmk_action_pseudo);
 906     }
 907 
 908     if (pcmk_is_set(type, pcmk__ar_unrunnable_first_blocks)
 909         && pcmk_is_set(filter, pcmk_action_runnable)
 910         && pcmk_is_set(then->flags, pcmk_action_runnable)
 911         && !pcmk_is_set(flags, pcmk_action_runnable)) {
 912 
 913         clear_action_flag_because(then, pcmk_action_runnable, first);
 914         clear_action_flag_because(then, pcmk_action_migratable, first);
 915     }
 916 
 917     if (pcmk_is_set(type, pcmk__ar_first_implies_then)
 918         && pcmk_is_set(filter, pcmk_action_optional)
 919         && pcmk_is_set(then->flags, pcmk_action_optional)
 920         && !pcmk_is_set(flags, pcmk_action_optional)
 921         && !pcmk_is_set(first->flags, pcmk_action_migratable)) {
 922 
 923         clear_action_flag_because(then, pcmk_action_optional, first);
 924     }
 925 
 926     if (pcmk_is_set(type, pcmk__ar_intermediate_stop)) {
 927         handle_restart_ordering(first, then, filter);
 928     }
 929 
 930     if (then_flags != then->flags) {
 931         pcmk__set_updated_flags(changed, first, pcmk__updated_then);
 932         pcmk__rsc_trace(then->rsc,
 933                         "%s on %s: flags are now %#.6x (was %#.6x) "
 934                         "because of 'first' %s (%#.6x)",
 935                         then->uuid, pcmk__node_name(then->node),
 936                         then->flags, then_flags, first->uuid, first->flags);
 937 
 938         if ((then->rsc != NULL) && (then->rsc->parent != NULL)) {
 939             // Required to handle "X_stop then X_start" for cloned groups
 940             pcmk__update_action_for_orderings(then, scheduler);
 941         }
 942     }
 943 
 944     if (first_flags != first->flags) {
 945         pcmk__set_updated_flags(changed, first, pcmk__updated_first);
 946         pcmk__rsc_trace(first->rsc,
 947                         "%s on %s: flags are now %#.6x (was %#.6x) "
 948                         "because of 'then' %s (%#.6x)",
 949                         first->uuid, pcmk__node_name(first->node),
 950                         first->flags, first_flags, then->uuid, then->flags);
 951     }
 952 
 953     return changed;
 954 }
 955 
 956 /*!
 957  * \internal
 958  * \brief Trace-log an action (optionally with its dependent actions)
 959  *
 960  * \param[in] pre_text  If not NULL, prefix the log with this plus ": "
 961  * \param[in] action    Action to log
 962  * \param[in] details   If true, recursively log dependent actions
 963  */
 964 void
 965 pcmk__log_action(const char *pre_text, const pcmk_action_t *action,
     /* [previous][next][first][last][top][bottom][index][help] */
 966                  bool details)
 967 {
 968     const char *node_uname = NULL;
 969     const char *node_uuid = NULL;
 970     const char *desc = NULL;
 971 
 972     CRM_CHECK(action != NULL, return);
 973 
 974     if (!pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 975         if (action->node != NULL) {
 976             node_uname = action->node->details->uname;
 977             node_uuid = action->node->details->id;
 978         } else {
 979             node_uname = "<none>";
 980         }
 981     }
 982 
 983     switch (pcmk_parse_action(action->task)) {
 984         case pcmk_action_fence:
 985         case pcmk_action_shutdown:
 986             if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
 987                 desc = "Pseudo ";
 988             } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
 989                 desc = "Optional ";
 990             } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
 991                 desc = "!!Non-Startable!! ";
 992             } else {
 993                desc = "(Provisional) ";
 994             }
 995             crm_trace("%s%s%sAction %d: %s%s%s%s%s%s",
 996                       ((pre_text == NULL)? "" : pre_text),
 997                       ((pre_text == NULL)? "" : ": "),
 998                       desc, action->id, action->uuid,
 999                       (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1000                       (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1001                       (node_uuid? ")" : ""));
1002             break;
1003         default:
1004             if (pcmk_is_set(action->flags, pcmk_action_optional)) {
1005                 desc = "Optional ";
1006             } else if (pcmk_is_set(action->flags, pcmk_action_pseudo)) {
1007                 desc = "Pseudo ";
1008             } else if (!pcmk_is_set(action->flags, pcmk_action_runnable)) {
1009                 desc = "!!Non-Startable!! ";
1010             } else {
1011                desc = "(Provisional) ";
1012             }
1013             crm_trace("%s%s%sAction %d: %s %s%s%s%s%s%s",
1014                       ((pre_text == NULL)? "" : pre_text),
1015                       ((pre_text == NULL)? "" : ": "),
1016                       desc, action->id, action->uuid,
1017                       (action->rsc? action->rsc->id : "<none>"),
1018                       (node_uname? "\ton " : ""), (node_uname? node_uname : ""),
1019                       (node_uuid? "\t\t(" : ""), (node_uuid? node_uuid : ""),
1020                       (node_uuid? ")" : ""));
1021             break;
1022     }
1023 
1024     if (details) {
1025         const GList *iter = NULL;
1026         const pcmk__related_action_t *other = NULL;
1027 
1028         crm_trace("\t\t====== Preceding Actions");
1029         for (iter = action->actions_before; iter != NULL; iter = iter->next) {
1030             other = (const pcmk__related_action_t *) iter->data;
1031             pcmk__log_action("\t\t", other->action, false);
1032         }
1033         crm_trace("\t\t====== Subsequent Actions");
1034         for (iter = action->actions_after; iter != NULL; iter = iter->next) {
1035             other = (const pcmk__related_action_t *) iter->data;
1036             pcmk__log_action("\t\t", other->action, false);
1037         }
1038         crm_trace("\t\t====== End");
1039 
1040     } else {
1041         crm_trace("\t\t(before=%d, after=%d)",
1042                   g_list_length(action->actions_before),
1043                   g_list_length(action->actions_after));
1044     }
1045 }
1046 
1047 /*!
1048  * \internal
1049  * \brief Create a new shutdown action for a node
1050  *
1051  * \param[in,out] node  Node being shut down
1052  *
1053  * \return Newly created shutdown action for \p node
1054  */
1055 pcmk_action_t *
1056 pcmk__new_shutdown_action(pcmk_node_t *node)
     /* [previous][next][first][last][top][bottom][index][help] */
1057 {
1058     char *shutdown_id = NULL;
1059     pcmk_action_t *shutdown_op = NULL;
1060 
1061     pcmk__assert(node != NULL);
1062 
1063     shutdown_id = crm_strdup_printf("%s-%s", PCMK_ACTION_DO_SHUTDOWN,
1064                                     node->details->uname);
1065 
1066     shutdown_op = custom_action(NULL, shutdown_id, PCMK_ACTION_DO_SHUTDOWN,
1067                                 node, FALSE, node->details->data_set);
1068 
1069     pcmk__order_stops_before_shutdown(node, shutdown_op);
1070     pcmk__insert_meta(shutdown_op, PCMK__META_OP_NO_WAIT, PCMK_VALUE_TRUE);
1071     return shutdown_op;
1072 }
1073 
1074 /*!
1075  * \internal
1076  * \brief Calculate and add an operation digest to XML
1077  *
1078  * Calculate an operation digest, which enables us to later determine when a
1079  * restart is needed due to the resource's parameters being changed, and add it
1080  * to given XML.
1081  *
1082  * \param[in]     op      Operation result from executor
1083  * \param[in,out] update  XML to add digest to
1084  */
1085 static void
1086 add_op_digest_to_xml(const lrmd_event_data_t *op, xmlNode *update)
     /* [previous][next][first][last][top][bottom][index][help] */
1087 {
1088     char *digest = NULL;
1089     xmlNode *args_xml = NULL;
1090 
1091     if (op->params == NULL) {
1092         return;
1093     }
1094     args_xml = pcmk__xe_create(NULL, PCMK_XE_PARAMETERS);
1095     g_hash_table_foreach(op->params, hash2field, args_xml);
1096     pcmk__filter_op_for_digest(args_xml);
1097     digest = calculate_operation_digest(args_xml, NULL);
1098     crm_xml_add(update, PCMK__XA_OP_DIGEST, digest);
1099     free_xml(args_xml);
1100     free(digest);
1101 }
1102 
1103 #define FAKE_TE_ID     "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
1104 
1105 /*!
1106  * \internal
1107  * \brief Create XML for resource operation history update
1108  *
1109  * \param[in,out] parent          Parent XML node to add to
1110  * \param[in,out] op              Operation event data
1111  * \param[in]     caller_version  DC feature set
1112  * \param[in]     target_rc       Expected result of operation
1113  * \param[in]     node            Name of node on which operation was performed
1114  * \param[in]     origin          Arbitrary description of update source
1115  *
1116  * \return Newly created XML node for history update
1117  */
1118 xmlNode *
1119 pcmk__create_history_xml(xmlNode *parent, lrmd_event_data_t *op,
     /* [previous][next][first][last][top][bottom][index][help] */
1120                          const char *caller_version, int target_rc,
1121                          const char *node, const char *origin)
1122 {
1123     char *key = NULL;
1124     char *magic = NULL;
1125     char *op_id = NULL;
1126     char *op_id_additional = NULL;
1127     char *local_user_data = NULL;
1128     const char *exit_reason = NULL;
1129 
1130     xmlNode *xml_op = NULL;
1131     const char *task = NULL;
1132 
1133     CRM_CHECK(op != NULL, return NULL);
1134     crm_trace("Creating history XML for %s-interval %s action for %s on %s "
1135               "(DC version: %s, origin: %s)",
1136               pcmk__readable_interval(op->interval_ms), op->op_type, op->rsc_id,
1137               ((node == NULL)? "no node" : node), caller_version, origin);
1138 
1139     task = op->op_type;
1140 
1141     /* Record a successful agent reload as a start, and a failed one as a
1142      * monitor, to make life easier for the scheduler when determining the
1143      * current state.
1144      *
1145      * @COMPAT We should check "reload" here only if the operation was for a
1146      * pre-OCF-1.1 resource agent, but we don't know that here, and we should
1147      * only ever get results for actions scheduled by us, so we can reasonably
1148      * assume any "reload" is actually a pre-1.1 agent reload.
1149      */
1150     if (pcmk__str_any_of(task, PCMK_ACTION_RELOAD, PCMK_ACTION_RELOAD_AGENT,
1151                          NULL)) {
1152         if (op->op_status == PCMK_EXEC_DONE) {
1153             task = PCMK_ACTION_START;
1154         } else {
1155             task = PCMK_ACTION_MONITOR;
1156         }
1157     }
1158 
1159     key = pcmk__op_key(op->rsc_id, task, op->interval_ms);
1160     if (pcmk__str_eq(task, PCMK_ACTION_NOTIFY, pcmk__str_none)) {
1161         const char *n_type = crm_meta_value(op->params, "notify_type");
1162         const char *n_task = crm_meta_value(op->params, "notify_operation");
1163 
1164         CRM_LOG_ASSERT(n_type != NULL);
1165         CRM_LOG_ASSERT(n_task != NULL);
1166         op_id = pcmk__notify_key(op->rsc_id, n_type, n_task);
1167 
1168         if (op->op_status != PCMK_EXEC_PENDING) {
1169             /* Ignore notify errors.
1170              *
1171              * @TODO It might be better to keep the correct result here, and
1172              * ignore it in process_graph_event().
1173              */
1174             lrmd__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
1175         }
1176 
1177     /* Migration history is preserved separately, which usually matters for
1178      * multiple nodes and is important for future cluster transitions.
1179      */
1180     } else if (pcmk__str_any_of(op->op_type, PCMK_ACTION_MIGRATE_TO,
1181                                 PCMK_ACTION_MIGRATE_FROM, NULL)) {
1182         op_id = strdup(key);
1183 
1184     } else if (did_rsc_op_fail(op, target_rc)) {
1185         op_id = pcmk__op_key(op->rsc_id, "last_failure", 0);
1186         if (op->interval_ms == 0) {
1187             /* Ensure 'last' gets updated, in case PCMK_META_RECORD_PENDING is
1188              * true
1189              */
1190             op_id_additional = pcmk__op_key(op->rsc_id, "last", 0);
1191         }
1192         exit_reason = op->exit_reason;
1193 
1194     } else if (op->interval_ms > 0) {
1195         op_id = strdup(key);
1196 
1197     } else {
1198         op_id = pcmk__op_key(op->rsc_id, "last", 0);
1199     }
1200 
1201   again:
1202     xml_op = pcmk__xe_first_child(parent, PCMK__XE_LRM_RSC_OP, PCMK_XA_ID,
1203                                   op_id);
1204     if (xml_op == NULL) {
1205         xml_op = pcmk__xe_create(parent, PCMK__XE_LRM_RSC_OP);
1206     }
1207 
1208     if (op->user_data == NULL) {
1209         crm_debug("Generating fake transition key for: " PCMK__OP_FMT
1210                   " %d from %s", op->rsc_id, op->op_type, op->interval_ms,
1211                   op->call_id, origin);
1212         local_user_data = pcmk__transition_key(-1, op->call_id, target_rc,
1213                                                FAKE_TE_ID);
1214         op->user_data = local_user_data;
1215     }
1216 
1217     if (magic == NULL) {
1218         magic = crm_strdup_printf("%d:%d;%s", op->op_status, op->rc,
1219                                   (const char *) op->user_data);
1220     }
1221 
1222     crm_xml_add(xml_op, PCMK_XA_ID, op_id);
1223     crm_xml_add(xml_op, PCMK__XA_OPERATION_KEY, key);
1224     crm_xml_add(xml_op, PCMK_XA_OPERATION, task);
1225     crm_xml_add(xml_op, PCMK_XA_CRM_DEBUG_ORIGIN, origin);
1226     crm_xml_add(xml_op, PCMK_XA_CRM_FEATURE_SET, caller_version);
1227     crm_xml_add(xml_op, PCMK__XA_TRANSITION_KEY, op->user_data);
1228     crm_xml_add(xml_op, PCMK__XA_TRANSITION_MAGIC, magic);
1229     crm_xml_add(xml_op, PCMK_XA_EXIT_REASON, pcmk__s(exit_reason, ""));
1230     crm_xml_add(xml_op, PCMK__META_ON_NODE, node); // For context during triage
1231 
1232     crm_xml_add_int(xml_op, PCMK__XA_CALL_ID, op->call_id);
1233     crm_xml_add_int(xml_op, PCMK__XA_RC_CODE, op->rc);
1234     crm_xml_add_int(xml_op, PCMK__XA_OP_STATUS, op->op_status);
1235     crm_xml_add_ms(xml_op, PCMK_META_INTERVAL, op->interval_ms);
1236 
1237     if (compare_version("2.1", caller_version) <= 0) {
1238         if (op->t_run || op->t_rcchange || op->exec_time || op->queue_time) {
1239             crm_trace("Timing data (" PCMK__OP_FMT
1240                       "): last=%u change=%u exec=%u queue=%u",
1241                       op->rsc_id, op->op_type, op->interval_ms,
1242                       op->t_run, op->t_rcchange, op->exec_time, op->queue_time);
1243 
1244             if ((op->interval_ms != 0) && (op->t_rcchange != 0)) {
1245                 // Recurring ops may have changed rc after initial run
1246                 crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE,
1247                                (long long) op->t_rcchange);
1248             } else {
1249                 crm_xml_add_ll(xml_op, PCMK_XA_LAST_RC_CHANGE,
1250                                (long long) op->t_run);
1251             }
1252 
1253             crm_xml_add_int(xml_op, PCMK_XA_EXEC_TIME, op->exec_time);
1254             crm_xml_add_int(xml_op, PCMK_XA_QUEUE_TIME, op->queue_time);
1255         }
1256     }
1257 
1258     if (pcmk__str_any_of(op->op_type, PCMK_ACTION_MIGRATE_TO,
1259                          PCMK_ACTION_MIGRATE_FROM, NULL)) {
1260         /* Record PCMK__META_MIGRATE_SOURCE and PCMK__META_MIGRATE_TARGET always
1261          * for migrate ops.
1262          */
1263         const char *name = PCMK__META_MIGRATE_SOURCE;
1264 
1265         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1266 
1267         name = PCMK__META_MIGRATE_TARGET;
1268         crm_xml_add(xml_op, name, crm_meta_value(op->params, name));
1269     }
1270 
1271     add_op_digest_to_xml(op, xml_op);
1272 
1273     if (op_id_additional) {
1274         free(op_id);
1275         op_id = op_id_additional;
1276         op_id_additional = NULL;
1277         goto again;
1278     }
1279 
1280     if (local_user_data) {
1281         free(local_user_data);
1282         op->user_data = NULL;
1283     }
1284     free(magic);
1285     free(op_id);
1286     free(key);
1287     return xml_op;
1288 }
1289 
1290 /*!
1291  * \internal
1292  * \brief Check whether an action shutdown-locks a resource to a node
1293  *
1294  * If the PCMK_OPT_SHUTDOWN_LOCK cluster property is set, resources will not be
1295  * recovered on a different node if cleanly stopped, and may start only on that
1296  * same node. This function checks whether that applies to a given action, so
1297  * that the transition graph can be marked appropriately.
1298  *
1299  * \param[in] action  Action to check
1300  *
1301  * \return true if \p action locks its resource to the action's node,
1302  *         otherwise false
1303  */
1304 bool
1305 pcmk__action_locks_rsc_to_node(const pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1306 {
1307     // Only resource actions taking place on resource's lock node are locked
1308     if ((action == NULL) || (action->rsc == NULL)
1309         || !pcmk__same_node(action->node, action->rsc->lock_node)) {
1310         return false;
1311     }
1312 
1313     /* During shutdown, only stops are locked (otherwise, another action such as
1314      * a demote would cause the controller to clear the lock)
1315      */
1316     if (action->node->details->shutdown && (action->task != NULL)
1317         && (strcmp(action->task, PCMK_ACTION_STOP) != 0)) {
1318         return false;
1319     }
1320 
1321     return true;
1322 }
1323 
1324 /* lowest to highest */
1325 static gint
1326 sort_action_id(gconstpointer a, gconstpointer b)
     /* [previous][next][first][last][top][bottom][index][help] */
1327 {
1328     const pcmk__related_action_t *action_wrapper2 = a;
1329     const pcmk__related_action_t *action_wrapper1 = b;
1330 
1331     if (a == NULL) {
1332         return 1;
1333     }
1334     if (b == NULL) {
1335         return -1;
1336     }
1337     if (action_wrapper1->action->id < action_wrapper2->action->id) {
1338         return 1;
1339     }
1340     if (action_wrapper1->action->id > action_wrapper2->action->id) {
1341         return -1;
1342     }
1343     return 0;
1344 }
1345 
1346 /*!
1347  * \internal
1348  * \brief Remove any duplicate action inputs, merging action flags
1349  *
1350  * \param[in,out] action  Action whose inputs should be checked
1351  */
1352 void
1353 pcmk__deduplicate_action_inputs(pcmk_action_t *action)
     /* [previous][next][first][last][top][bottom][index][help] */
1354 {
1355     GList *item = NULL;
1356     GList *next = NULL;
1357     pcmk__related_action_t *last_input = NULL;
1358 
1359     action->actions_before = g_list_sort(action->actions_before,
1360                                          sort_action_id);
1361     for (item = action->actions_before; item != NULL; item = next) {
1362         pcmk__related_action_t *input = item->data;
1363 
1364         next = item->next;
1365         if ((last_input != NULL)
1366             && (input->action->id == last_input->action->id)) {
1367             crm_trace("Input %s (%d) duplicate skipped for action %s (%d)",
1368                       input->action->uuid, input->action->id,
1369                       action->uuid, action->id);
1370 
1371             /* For the purposes of scheduling, the ordering flags no longer
1372              * matter, but crm_simulate looks at certain ones when creating a
1373              * dot graph. Combining the flags is sufficient for that purpose.
1374              */
1375             last_input->type |= input->type;
1376             if (input->state == pe_link_dumped) {
1377                 last_input->state = pe_link_dumped;
1378             }
1379 
1380             free(item->data);
1381             action->actions_before = g_list_delete_link(action->actions_before,
1382                                                         item);
1383         } else {
1384             last_input = input;
1385             input->state = pe_link_not_dumped;
1386         }
1387     }
1388 }
1389 
1390 /*!
1391  * \internal
1392  * \brief Output all scheduled actions
1393  *
1394  * \param[in,out] scheduler  Scheduler data
1395  */
1396 void
1397 pcmk__output_actions(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
1398 {
1399     pcmk__output_t *out = scheduler->priv;
1400 
1401     // Output node (non-resource) actions
1402     for (GList *iter = scheduler->actions; iter != NULL; iter = iter->next) {
1403         char *node_name = NULL;
1404         char *task = NULL;
1405         pcmk_action_t *action = (pcmk_action_t *) iter->data;
1406 
1407         if (action->rsc != NULL) {
1408             continue; // Resource actions will be output later
1409 
1410         } else if (pcmk_is_set(action->flags, pcmk_action_optional)) {
1411             continue; // This action was not scheduled
1412         }
1413 
1414         if (pcmk__str_eq(action->task, PCMK_ACTION_DO_SHUTDOWN,
1415                          pcmk__str_none)) {
1416             task = strdup("Shutdown");
1417 
1418         } else if (pcmk__str_eq(action->task, PCMK_ACTION_STONITH,
1419                                 pcmk__str_none)) {
1420             const char *op = g_hash_table_lookup(action->meta,
1421                                                  PCMK__META_STONITH_ACTION);
1422 
1423             task = crm_strdup_printf("Fence (%s)", op);
1424 
1425         } else {
1426             continue; // Don't display other node action types
1427         }
1428 
1429         if (pcmk__is_guest_or_bundle_node(action->node)) {
1430             const pcmk_resource_t *remote = action->node->details->remote_rsc;
1431 
1432             node_name = crm_strdup_printf("%s (resource: %s)",
1433                                           pcmk__node_name(action->node),
1434                                           remote->container->id);
1435         } else if (action->node != NULL) {
1436             node_name = crm_strdup_printf("%s", pcmk__node_name(action->node));
1437         }
1438 
1439         out->message(out, "node-action", task, node_name, action->reason);
1440 
1441         free(node_name);
1442         free(task);
1443     }
1444 
1445     // Output resource actions
1446     for (GList *iter = scheduler->resources; iter != NULL; iter = iter->next) {
1447         pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1448 
1449         rsc->cmds->output_actions(rsc);
1450     }
1451 }
1452 
1453 /*!
1454  * \internal
1455  * \brief Get action name needed to compare digest for configuration changes
1456  *
1457  * \param[in] task         Action name from history
1458  * \param[in] interval_ms  Action interval (in milliseconds)
1459  *
1460  * \return Action name whose digest should be compared
1461  */
1462 static const char *
1463 task_for_digest(const char *task, guint interval_ms)
     /* [previous][next][first][last][top][bottom][index][help] */
1464 {
1465     /* Certain actions need to be compared against the parameters used to start
1466      * the resource.
1467      */
1468     if ((interval_ms == 0)
1469         && pcmk__str_any_of(task, PCMK_ACTION_MONITOR, PCMK_ACTION_MIGRATE_FROM,
1470                             PCMK_ACTION_PROMOTE, NULL)) {
1471         task = PCMK_ACTION_START;
1472     }
1473     return task;
1474 }
1475 
1476 /*!
1477  * \internal
1478  * \brief Check whether only sanitized parameters to an action changed
1479  *
1480  * When collecting CIB files for troubleshooting, crm_report will mask
1481  * sensitive resource parameters. If simulations were run using that, affected
1482  * resources would appear to need a restart, which would complicate
1483  * troubleshooting. To avoid that, we save a "secure digest" of non-sensitive
1484  * parameters. This function used that digest to check whether only masked
1485  * parameters are different.
1486  *
1487  * \param[in] xml_op       Resource history entry with secure digest
1488  * \param[in] digest_data  Operation digest information being compared
1489  * \param[in] scheduler    Scheduler data
1490  *
1491  * \return true if only sanitized parameters changed, otherwise false
1492  */
1493 static bool
1494 only_sanitized_changed(const xmlNode *xml_op,
     /* [previous][next][first][last][top][bottom][index][help] */
1495                        const pcmk__op_digest_t *digest_data,
1496                        const pcmk_scheduler_t *scheduler)
1497 {
1498     const char *digest_secure = NULL;
1499 
1500     if (!pcmk_is_set(scheduler->flags, pcmk_sched_sanitized)) {
1501         // The scheduler is not being run as a simulation
1502         return false;
1503     }
1504 
1505     digest_secure = crm_element_value(xml_op, PCMK__XA_OP_SECURE_DIGEST);
1506 
1507     return (digest_data->rc != pcmk__digest_match) && (digest_secure != NULL)
1508            && (digest_data->digest_secure_calc != NULL)
1509            && (strcmp(digest_data->digest_secure_calc, digest_secure) == 0);
1510 }
1511 
1512 /*!
1513  * \internal
1514  * \brief Force a restart due to a configuration change
1515  *
1516  * \param[in,out] rsc          Resource that action is for
1517  * \param[in]     task         Name of action whose configuration changed
1518  * \param[in]     interval_ms  Action interval (in milliseconds)
1519  * \param[in,out] node         Node where resource should be restarted
1520  */
1521 static void
1522 force_restart(pcmk_resource_t *rsc, const char *task, guint interval_ms,
     /* [previous][next][first][last][top][bottom][index][help] */
1523               pcmk_node_t *node)
1524 {
1525     char *key = pcmk__op_key(rsc->id, task, interval_ms);
1526     pcmk_action_t *required = custom_action(rsc, key, task, NULL, FALSE,
1527                                             rsc->cluster);
1528 
1529     pe_action_set_reason(required, "resource definition change", true);
1530     trigger_unfencing(rsc, node, "Device parameters changed", NULL,
1531                       rsc->cluster);
1532 }
1533 
1534 /*!
1535  * \internal
1536  * \brief Schedule a reload of a resource on a node
1537  *
1538  * \param[in,out] data       Resource to reload
1539  * \param[in]     user_data  Where resource should be reloaded
1540  */
1541 static void
1542 schedule_reload(gpointer data, gpointer user_data)
     /* [previous][next][first][last][top][bottom][index][help] */
1543 {
1544     pcmk_resource_t *rsc = data;
1545     const pcmk_node_t *node = user_data;
1546     pcmk_action_t *reload = NULL;
1547 
1548     // For collective resources, just call recursively for children
1549     if (rsc->variant > pcmk_rsc_variant_primitive) {
1550         g_list_foreach(rsc->children, schedule_reload, user_data);
1551         return;
1552     }
1553 
1554     // Skip the reload in certain situations
1555     if ((node == NULL)
1556         || !pcmk_is_set(rsc->flags, pcmk_rsc_managed)
1557         || pcmk_is_set(rsc->flags, pcmk_rsc_failed)) {
1558         pcmk__rsc_trace(rsc, "Skip reload of %s:%s%s %s",
1559                         rsc->id,
1560                         pcmk_is_set(rsc->flags, pcmk_rsc_managed)? "" : " unmanaged",
1561                         pcmk_is_set(rsc->flags, pcmk_rsc_failed)? " failed" : "",
1562                         (node == NULL)? "inactive" : node->details->uname);
1563         return;
1564     }
1565 
1566     /* If a resource's configuration changed while a start was pending,
1567      * force a full restart instead of a reload.
1568      */
1569     if (pcmk_is_set(rsc->flags, pcmk_rsc_start_pending)) {
1570         pcmk__rsc_trace(rsc,
1571                         "%s: preventing agent reload because start pending",
1572                         rsc->id);
1573         custom_action(rsc, stop_key(rsc), PCMK_ACTION_STOP, node, FALSE,
1574                       rsc->cluster);
1575         return;
1576     }
1577 
1578     // Schedule the reload
1579     pcmk__set_rsc_flags(rsc, pcmk_rsc_reload);
1580     reload = custom_action(rsc, reload_key(rsc), PCMK_ACTION_RELOAD_AGENT, node,
1581                            FALSE, rsc->cluster);
1582     pe_action_set_reason(reload, "resource definition change", FALSE);
1583 
1584     // Set orderings so that a required stop or demote cancels the reload
1585     pcmk__new_ordering(NULL, NULL, reload, rsc, stop_key(rsc), NULL,
1586                        pcmk__ar_ordered|pcmk__ar_then_cancels_first,
1587                        rsc->cluster);
1588     pcmk__new_ordering(NULL, NULL, reload, rsc, demote_key(rsc), NULL,
1589                        pcmk__ar_ordered|pcmk__ar_then_cancels_first,
1590                        rsc->cluster);
1591 }
1592 
1593 /*!
1594  * \internal
1595  * \brief Handle any configuration change for an action
1596  *
1597  * Given an action from resource history, if the resource's configuration
1598  * changed since the action was done, schedule any actions needed (restart,
1599  * reload, unfencing, rescheduling recurring actions, etc.).
1600  *
1601  * \param[in,out] rsc     Resource that action is for
1602  * \param[in,out] node    Node that action was on
1603  * \param[in]     xml_op  Action XML from resource history
1604  *
1605  * \return true if action configuration changed, otherwise false
1606  */
1607 bool
1608 pcmk__check_action_config(pcmk_resource_t *rsc, pcmk_node_t *node,
     /* [previous][next][first][last][top][bottom][index][help] */
1609                           const xmlNode *xml_op)
1610 {
1611     guint interval_ms = 0;
1612     const char *task = NULL;
1613     const pcmk__op_digest_t *digest_data = NULL;
1614 
1615     CRM_CHECK((rsc != NULL) && (node != NULL) && (xml_op != NULL),
1616               return false);
1617 
1618     task = crm_element_value(xml_op, PCMK_XA_OPERATION);
1619     CRM_CHECK(task != NULL, return false);
1620 
1621     crm_element_value_ms(xml_op, PCMK_META_INTERVAL, &interval_ms);
1622 
1623     // If this is a recurring action, check whether it has been orphaned
1624     if (interval_ms > 0) {
1625         if (pcmk__find_action_config(rsc, task, interval_ms, false) != NULL) {
1626             pcmk__rsc_trace(rsc,
1627                             "%s-interval %s for %s on %s is in configuration",
1628                             pcmk__readable_interval(interval_ms), task, rsc->id,
1629                             pcmk__node_name(node));
1630         } else if (pcmk_is_set(rsc->cluster->flags,
1631                                pcmk_sched_cancel_removed_actions)) {
1632             pcmk__schedule_cancel(rsc,
1633                                   crm_element_value(xml_op, PCMK__XA_CALL_ID),
1634                                   task, interval_ms, node, "orphan");
1635             return true;
1636         } else {
1637             pcmk__rsc_debug(rsc, "%s-interval %s for %s on %s is orphaned",
1638                             pcmk__readable_interval(interval_ms), task, rsc->id,
1639                             pcmk__node_name(node));
1640             return true;
1641         }
1642     }
1643 
1644     crm_trace("Checking %s-interval %s for %s on %s for configuration changes",
1645               pcmk__readable_interval(interval_ms), task, rsc->id,
1646               pcmk__node_name(node));
1647     task = task_for_digest(task, interval_ms);
1648     digest_data = rsc_action_digest_cmp(rsc, xml_op, node, rsc->cluster);
1649 
1650     if (only_sanitized_changed(xml_op, digest_data, rsc->cluster)) {
1651         if (!pcmk__is_daemon && (rsc->cluster->priv != NULL)) {
1652             pcmk__output_t *out = rsc->cluster->priv;
1653 
1654             out->info(out,
1655                       "Only 'private' parameters to %s-interval %s for %s "
1656                       "on %s changed: %s",
1657                       pcmk__readable_interval(interval_ms), task, rsc->id,
1658                       pcmk__node_name(node),
1659                       crm_element_value(xml_op, PCMK__XA_TRANSITION_MAGIC));
1660         }
1661         return false;
1662     }
1663 
1664     switch (digest_data->rc) {
1665         case pcmk__digest_restart:
1666             crm_log_xml_debug(digest_data->params_restart, "params:restart");
1667             force_restart(rsc, task, interval_ms, node);
1668             return true;
1669 
1670         case pcmk__digest_unknown:
1671         case pcmk__digest_mismatch:
1672             // Changes that can potentially be handled by an agent reload
1673 
1674             if (interval_ms > 0) {
1675                 /* Recurring actions aren't reloaded per se, they are just
1676                  * re-scheduled so the next run uses the new parameters.
1677                  * The old instance will be cancelled automatically.
1678                  */
1679                 crm_log_xml_debug(digest_data->params_all, "params:reschedule");
1680                 pcmk__reschedule_recurring(rsc, task, interval_ms, node);
1681 
1682             } else if (crm_element_value(xml_op,
1683                                          PCMK__XA_OP_RESTART_DIGEST) != NULL) {
1684                 // Agent supports reload, so use it
1685                 trigger_unfencing(rsc, node,
1686                                   "Device parameters changed (reload)", NULL,
1687                                   rsc->cluster);
1688                 crm_log_xml_debug(digest_data->params_all, "params:reload");
1689                 schedule_reload((gpointer) rsc, (gpointer) node);
1690 
1691             } else {
1692                 pcmk__rsc_trace(rsc,
1693                                 "Restarting %s "
1694                                 "because agent doesn't support reload",
1695                                 rsc->id);
1696                 crm_log_xml_debug(digest_data->params_restart,
1697                                   "params:restart");
1698                 force_restart(rsc, task, interval_ms, node);
1699             }
1700             return true;
1701 
1702         default:
1703             break;
1704     }
1705     return false;
1706 }
1707 
1708 /*!
1709  * \internal
1710  * \brief Create a list of resource's action history entries, sorted by call ID
1711  *
1712  * \param[in]  rsc_entry    Resource's \c PCMK__XE_LRM_RSC_OP status XML
1713  * \param[out] start_index  Where to store index of start-like action, if any
1714  * \param[out] stop_index   Where to store index of stop action, if any
1715  */
1716 static GList *
1717 rsc_history_as_list(const xmlNode *rsc_entry, int *start_index, int *stop_index)
     /* [previous][next][first][last][top][bottom][index][help] */
1718 {
1719     GList *ops = NULL;
1720 
1721     for (xmlNode *rsc_op = pcmk__xe_first_child(rsc_entry, PCMK__XE_LRM_RSC_OP,
1722                                                 NULL, NULL);
1723          rsc_op != NULL; rsc_op = pcmk__xe_next_same(rsc_op)) {
1724 
1725         ops = g_list_prepend(ops, rsc_op);
1726     }
1727     ops = g_list_sort(ops, sort_op_by_callid);
1728     calculate_active_ops(ops, start_index, stop_index);
1729     return ops;
1730 }
1731 
1732 /*!
1733  * \internal
1734  * \brief Process a resource's action history from the CIB status
1735  *
1736  * Given a resource's action history, if the resource's configuration
1737  * changed since the actions were done, schedule any actions needed (restart,
1738  * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1739  * (This also cancels recurring actions for maintenance mode, which is not
1740  * entirely related but convenient to do here.)
1741  *
1742  * \param[in]     rsc_entry  Resource's \c PCMK__XE_LRM_RSC_OP status XML
1743  * \param[in,out] rsc        Resource whose history is being processed
1744  * \param[in,out] node       Node whose history is being processed
1745  */
1746 static void
1747 process_rsc_history(const xmlNode *rsc_entry, pcmk_resource_t *rsc,
     /* [previous][next][first][last][top][bottom][index][help] */
1748                     pcmk_node_t *node)
1749 {
1750     int offset = -1;
1751     int stop_index = 0;
1752     int start_index = 0;
1753     GList *sorted_op_list = NULL;
1754 
1755     if (pcmk_is_set(rsc->flags, pcmk_rsc_removed)) {
1756         if (pcmk__is_anonymous_clone(pe__const_top_resource(rsc, false))) {
1757             pcmk__rsc_trace(rsc,
1758                             "Skipping configuration check "
1759                             "for orphaned clone instance %s",
1760                             rsc->id);
1761         } else {
1762             pcmk__rsc_trace(rsc,
1763                             "Skipping configuration check and scheduling "
1764                             "clean-up for orphaned resource %s", rsc->id);
1765             pcmk__schedule_cleanup(rsc, node, false);
1766         }
1767         return;
1768     }
1769 
1770     if (pe_find_node_id(rsc->running_on, node->details->id) == NULL) {
1771         if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, false)) {
1772             pcmk__schedule_cleanup(rsc, node, false);
1773         }
1774         pcmk__rsc_trace(rsc,
1775                         "Skipping configuration check for %s "
1776                         "because no longer active on %s",
1777                         rsc->id, pcmk__node_name(node));
1778         return;
1779     }
1780 
1781     pcmk__rsc_trace(rsc, "Checking for configuration changes for %s on %s",
1782                     rsc->id, pcmk__node_name(node));
1783 
1784     if (pcmk__rsc_agent_changed(rsc, node, rsc_entry, true)) {
1785         pcmk__schedule_cleanup(rsc, node, false);
1786     }
1787 
1788     sorted_op_list = rsc_history_as_list(rsc_entry, &start_index, &stop_index);
1789     if (start_index < stop_index) {
1790         return; // Resource is stopped
1791     }
1792 
1793     for (GList *iter = sorted_op_list; iter != NULL; iter = iter->next) {
1794         xmlNode *rsc_op = (xmlNode *) iter->data;
1795         const char *task = NULL;
1796         guint interval_ms = 0;
1797 
1798         if (++offset < start_index) {
1799             // Skip actions that happened before a start
1800             continue;
1801         }
1802 
1803         task = crm_element_value(rsc_op, PCMK_XA_OPERATION);
1804         crm_element_value_ms(rsc_op, PCMK_META_INTERVAL, &interval_ms);
1805 
1806         if ((interval_ms > 0)
1807             && (pcmk_is_set(rsc->flags, pcmk_rsc_maintenance)
1808                 || node->details->maintenance)) {
1809             // Maintenance mode cancels recurring operations
1810             pcmk__schedule_cancel(rsc,
1811                                   crm_element_value(rsc_op, PCMK__XA_CALL_ID),
1812                                   task, interval_ms, node, "maintenance mode");
1813 
1814         } else if ((interval_ms > 0)
1815                    || pcmk__strcase_any_of(task, PCMK_ACTION_MONITOR,
1816                                            PCMK_ACTION_START,
1817                                            PCMK_ACTION_PROMOTE,
1818                                            PCMK_ACTION_MIGRATE_FROM, NULL)) {
1819             /* If a resource operation failed, and the operation's definition
1820              * has changed, clear any fail count so they can be retried fresh.
1821              */
1822 
1823             if (pe__bundle_needs_remote_name(rsc)) {
1824                 /* We haven't assigned resources to nodes yet, so if the
1825                  * REMOTE_CONTAINER_HACK is used, we may calculate the digest
1826                  * based on the literal "#uname" value rather than the properly
1827                  * substituted value. That would mistakenly make the action
1828                  * definition appear to have been changed. Defer the check until
1829                  * later in this case.
1830                  */
1831                 pe__add_param_check(rsc_op, rsc, node, pcmk__check_active,
1832                                     rsc->cluster);
1833 
1834             } else if (pcmk__check_action_config(rsc, node, rsc_op)
1835                        && (pe_get_failcount(node, rsc, NULL, pcmk__fc_effective,
1836                                             NULL) != 0)) {
1837                 pe__clear_failcount(rsc, node, "action definition changed",
1838                                     rsc->cluster);
1839             }
1840         }
1841     }
1842     g_list_free(sorted_op_list);
1843 }
1844 
1845 /*!
1846  * \internal
1847  * \brief Process a node's action history from the CIB status
1848  *
1849  * Given a node's resource history, if the resource's configuration changed
1850  * since the actions were done, schedule any actions needed (restart,
1851  * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1852  * (This also cancels recurring actions for maintenance mode, which is not
1853  * entirely related but convenient to do here.)
1854  *
1855  * \param[in,out] node      Node whose history is being processed
1856  * \param[in]     lrm_rscs  Node's \c PCMK__XE_LRM_RESOURCES from CIB status XML
1857  */
1858 static void
1859 process_node_history(pcmk_node_t *node, const xmlNode *lrm_rscs)
     /* [previous][next][first][last][top][bottom][index][help] */
1860 {
1861     crm_trace("Processing node history for %s", pcmk__node_name(node));
1862     for (const xmlNode *rsc_entry = pcmk__xe_first_child(lrm_rscs,
1863                                                          PCMK__XE_LRM_RESOURCE,
1864                                                          NULL, NULL);
1865          rsc_entry != NULL; rsc_entry = pcmk__xe_next_same(rsc_entry)) {
1866 
1867         if (rsc_entry->children != NULL) {
1868             GList *result = pcmk__rscs_matching_id(pcmk__xe_id(rsc_entry),
1869                                                    node->details->data_set);
1870 
1871             for (GList *iter = result; iter != NULL; iter = iter->next) {
1872                 pcmk_resource_t *rsc = (pcmk_resource_t *) iter->data;
1873 
1874                 if (pcmk__is_primitive(rsc)) {
1875                     process_rsc_history(rsc_entry, rsc, node);
1876                 }
1877             }
1878             g_list_free(result);
1879         }
1880     }
1881 }
1882 
1883 // XPath to find a node's resource history
1884 #define XPATH_NODE_HISTORY "/" PCMK_XE_CIB "/" PCMK_XE_STATUS   \
1885                            "/" PCMK__XE_NODE_STATE              \
1886                            "[@" PCMK_XA_UNAME "='%s']"          \
1887                            "/" PCMK__XE_LRM "/" PCMK__XE_LRM_RESOURCES
1888 
1889 /*!
1890  * \internal
1891  * \brief Process any resource configuration changes in the CIB status
1892  *
1893  * Go through all nodes' resource history, and if a resource's configuration
1894  * changed since its actions were done, schedule any actions needed (restart,
1895  * reload, unfencing, rescheduling recurring actions, clean-up, etc.).
1896  * (This also cancels recurring actions for maintenance mode, which is not
1897  * entirely related but convenient to do here.)
1898  *
1899  * \param[in,out] scheduler  Scheduler data
1900  */
1901 void
1902 pcmk__handle_rsc_config_changes(pcmk_scheduler_t *scheduler)
     /* [previous][next][first][last][top][bottom][index][help] */
1903 {
1904     crm_trace("Check resource and action configuration for changes");
1905 
1906     /* Rather than iterate through the status section, iterate through the nodes
1907      * and search for the appropriate status subsection for each. This skips
1908      * orphaned nodes and lets us eliminate some cases before searching the XML.
1909      */
1910     for (GList *iter = scheduler->nodes; iter != NULL; iter = iter->next) {
1911         pcmk_node_t *node = (pcmk_node_t *) iter->data;
1912 
1913         /* Don't bother checking actions for a node that can't run actions ...
1914          * unless it's in maintenance mode, in which case we still need to
1915          * cancel any existing recurring monitors.
1916          */
1917         if (node->details->maintenance
1918             || pcmk__node_available(node, false, false)) {
1919 
1920             char *xpath = NULL;
1921             xmlNode *history = NULL;
1922 
1923             xpath = crm_strdup_printf(XPATH_NODE_HISTORY, node->details->uname);
1924             history = get_xpath_object(xpath, scheduler->input, LOG_NEVER);
1925             free(xpath);
1926 
1927             process_node_history(node, history);
1928         }
1929     }
1930 }

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