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

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