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

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