LCOV - code coverage report
Current view: top level - pacemaker - pcmk_sched_actions.c (source / functions) Hit Total Coverage
Test: Pacemaker code coverage Lines: 0 716 0.0 %
Date: 2024-05-07 11:09:47 Functions: 0 27 0.0 %

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

Generated by: LCOV version 1.14