root/lib/pacemaker/pcmk_sched_actions.c

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

DEFINITIONS

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

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

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